Bilibili-Evolved/min/live-socket.min.js
2020-02-28 14:21:59 +08:00

1 line
4.4 KiB
JavaScript

(()=>{return(e,t)=>{const s={json:0,heartBeatResponse:1,buffer:2};const r={heartBeat:2,heartBeatResponse:3,message:5,enterRoom:7,enterRoomResponse:8};class o{constructor(){this.textEncoder=new TextEncoder;this.textDecoder=new TextDecoder}readInt(e,t,s){let r=0;for(let o=s-1;o>=0;o--){r+=Math.pow(256,s-o-1)*e[t+o]}return r}writeInt(e,t,s,r){let o=0;while(o<s){e[t+o]=r/Math.pow(256,s-o-1);o++}}encode(e,t){const s=this.textEncoder.encode(e);const o=16+s.byteLength;const i=[0,0,0,0,0,16,0,1,0,0,0,r[t],0,0,0,1];this.writeInt(i,0,4,o);return new Uint8Array(i.concat(...s)).buffer}decode(e){const o=async e=>{const i={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=[i];if(i.packetLength<e.length){n.push(...await o(e.slice(i.packetLength)))}if(i.operation===r.message){const r=e.slice(i.headerLength,i.packetLength);if(i.protocolVersion===s.buffer){const{pako:e}=await t.importAsync("pako-inflate");i.messages=(await o(e.inflate(r))).map(e=>e.messages[0])}else if(i.protocolVersion===s.json){i.messages=[JSON.parse(this.textDecoder.decode(r))]}}else if(i.operation===r.heartBeatResponse){i.heartBeatResponse={count:this.readInt(e,16,4)}}return n};return new Promise(t=>{const s=new FileReader;s.onload=(async e=>{const s=new Uint8Array(e.target.result);t(await o(s))});s.readAsArrayBuffer(e)})}}class i{constructor(){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=>{return 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[o,i,n=0]=r.split(":").reverse().map(_.unary(parseInt));const a=Number(new Date);this.startTime=a-n*1e3*3600-i*60*1e3-o*1e3;e(this.startTime)}})})}}class n extends EventTarget{constructor(e){super();this.roomID=e;this.retryInterval=200;this.autoRetry=true;this.liveTime=new i;this.bufferHelper=new o;this.stopRequested=false;window.addEventListener("unload",()=>this.stop())}heartBeat(){this.webSocket.send(this.bufferHelper.encode("","heartBeat"))}restart(){this.dispatchEvent(new CustomEvent("restart"));if(!this.stopRequested&&this.autoRetry){console.log(`Live Socket: unexpected disconnect, retry in ${this.retryInterval}ms`);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",[]);let s="broadcastlv.chat.bilibili.com";if(t.length>0){s=t[0].host}if(this.webSocket&&(this.webSocket.readyState===WebSocket.CONNECTING||this.webSocket.readyState===WebSocket.OPEN)){this.stop()}this.webSocket=new WebSocket(`wss://${s}/sub`);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 r.enterRoomResponse:{if(this.heartBeatTimer){clearInterval(this.heartBeatTimer)}this.heartBeatTimer=setInterval(()=>{this.heartBeat()},30*1e3);break}case r.heartBeatResponse:{this.dispatchEvent(new CustomEvent("heartBeatResponse",{detail:t.heartBeatResponse.count}));break}case r.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.close()}}}return{export:{LiveSocket:n}}}})();