// ==UserScript== // @name bilibili 直播 HTML5 播放器 // @namespace https://www.kindjeff.com/ // @version 2017.2.24 // @description B 站的直播的 HTML5 播放器 // @author kindJeff // @match http://live.bilibili.com/* // @match https://live.bilibili.com/* // @require https://cdn.bootcss.com/hls.js/0.6.21/hls.min.js // @require https://cdn.rawgit.com/weizhenye/Danmaku/b2ae44f7dcd28b27c31e5db5b512b9474d0396c7/dist/danmaku.min.js // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/27239/bilibili%20%E7%9B%B4%E6%92%AD%20HTML5%20%E6%92%AD%E6%94%BE%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/27239/bilibili%20%E7%9B%B4%E6%92%AD%20HTML5%20%E6%92%AD%E6%94%BE%E5%99%A8.meta.js // ==/UserScript== var room_id; setTimeout(function(){ //var xhr = new XMLHttpRequest(); //xhr.onreadystatechange=function(){ // if (xhr.readyState==4 && xhr.status==200){ // eval(xhr.responseText); var link = $('#player_object').children('[name="flashvars"]').val(); room_id = link.match(/cid=.*?&/)[0].slice(4,-1); get_url_and_replace_player(room_id); init_danmaku(); set_danmu_control(); click_list(); // } //}; //xhr.open('GET', 'https://raw.githubusercontent.com/weizhenye/Danmaku/master/dist/danmaku.min.js'); //xhr.send(); }, 2000); function get_url_and_replace_player(room_id){ var api_url = 'https://api.live.bilibili.com/api/playurl?platform=h5&cid=' + room_id; $.ajax({ url: api_url, type: "GET", dataType: 'json', success: function(data){ replace_player(data.data); if(window.df_danmu_ws!==undefined){ window.i_close_it_myself = true; window.df_danmu_ws.close(); window.df_danmu_ws = undefined; } var df_domain = 'broadcastlv.chat.bilibili.com'; var df_portobj = {'ws':7170, 'wss':7172}; window.df_danmu_ws = new DanmuSocket(parseInt(room_id), df_domain, df_portobj); window.df_danmu_ws.setListener(danmuListener); } }); } function replace_player(m3u8_url){ var w = $('#js-player-decorator').width(); var h = $('#js-player-decorator').height(); remove_player(); var player = document.createElement('video'); player.id = 'h5_player'; player.style.width = '100%'; player.style.height = '100%'; player.style.position = 'absolute'; player.setAttribute('controls', 'controls'); document.getElementById('js-player-decorator').appendChild(player); if(Hls.isSupported()) { var video = document.getElementById('h5_player'); var hls = new Hls(); hls.loadSource(m3u8_url); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED,function() { video.play(); }); } } function remove_player(){ var flash_player = document.getElementById('player_object'); if(flash_player!==null) flash_player.remove(); var html5_player = document.getElementById('h5_player'); if(html5_player!==null) html5_player.remove(); } function click_list(){ if(window.location.pathname==='/'){ $($('[role="list"]')[0]).children().on('click', function(){ var room_id = $(this).attr('data-cid'); get_url_and_replace_player(room_id); }); } } /* danmaku */ const rawHeaderLen = 16; const packetOffset = 0; const headerOffset = 4; const verOffset = 6; const opOffset = 8; const seqOffset = 12; var pako = window.pako; var textDecoder = getDecoder(true); var textEncoder = getEncoder(); var heartbeatInterval; function getDecoder (isUseful) { if(window['TextDecoder'] && isUseful) { return new window['TextDecoder'](); } else { return { decode: (buf) => { return decodeURIComponent(window.escape(String.fromCharCode.apply(null, new Uint8Array(buf)))); } } } } function getEncoder () { if(window['TextEncoder']) { return new window['TextEncoder'](); } else { return { encode: (str) => { let buf = new ArrayBuffer(str.length); let bufView = new Uint8Array(buf); for (let i = 0, strlen = str.length; i < strlen; i++) { bufView[i] = str.charCodeAt(i); } return bufView; } } } } function mergeArrayBuffer(ab1, ab2) { var u81 = new Uint8Array(ab1), u82 = new Uint8Array(ab2), res = new Uint8Array(ab1.byteLength + ab2.byteLength); res.set(u81, 0); res.set(u82, ab1.byteLength); return res.buffer; } class DanmuSocket { constructor (roomid,domain,portobj) { const ws = window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws'; const port = portobj[ws]; this.connection = new WebSocket(ws + "://"+ domain +":"+ port +"/sub"); this.connection.binaryType = 'arraybuffer'; this.connection.onopen = this.firstConnection.bind(this); this.connection.onmessage = onMessage.bind(this); this.connection.onclose = onClose.bind(this); this.connection.onerror = onError.bind(this); this.roomid = roomid } firstConnection () { console.log("Danmu WebSocket Server Connected."); console.log("Handshaking..."); var token = JSON.stringify({ 'uid': 0, 'roomid': this.roomid }); var headerBuf = new ArrayBuffer(rawHeaderLen); var headerView = new DataView(headerBuf, 0); var bodyBuf = textEncoder.encode(token); headerView.setInt32(packetOffset, rawHeaderLen + bodyBuf.byteLength); headerView.setInt16(headerOffset, rawHeaderLen); headerView.setInt16(verOffset, 1); headerView.setInt32(opOffset, 7); headerView.setInt32(seqOffset, 1); this.connection.send(mergeArrayBuffer(headerBuf, bodyBuf)); } heartBeat () { var headerBuf = new ArrayBuffer(rawHeaderLen); var headerView = new DataView(headerBuf, 0); headerView.setInt32(packetOffset, rawHeaderLen); headerView.setInt16(headerOffset, rawHeaderLen); headerView.setInt16(verOffset, 1); headerView.setInt32(opOffset, 2); headerView.setInt32(seqOffset, 1); this.connection.send(headerBuf); } closeHeartBeat () { clearInterval(this.heartBeating); } send (data) { this.connection.send(data); } close () { this.connection.close(); } setListener (listener) { this._listener = listener; } } function onMessage (evt) { var data = evt.data; var dataView = new DataView(data, 0); var packetLen = dataView.getInt32(packetOffset); var headerLen = dataView.getInt16(headerOffset); var ver = dataView.getInt16(verOffset); var op = dataView.getInt32(opOffset); var seq = dataView.getInt32(seqOffset); switch(op) { case 8: this.heartBeat(); heartbeatInterval = setInterval(this.heartBeat.bind(this), 30 * 1000); break; case 3: // console.log("online: " + dataView.getInt32(16)); if (this._listener) this._listener('online', dataView.getInt32(16)); break; case 5: var packetView = dataView; var msg = data; var msgBody; for (var offset=0; offset')!==-1) {rank.replace('>', '>')} var msg = data.info[1]; // console.log(u_name,uid,lv,rank,msg); var comment_div = '
UL '+lv+'

用户等级:'+lv+'

排名:'+rank+'

'+u_name+' : '+msg+'
'; $(comment_div).appendTo('#chat-msg-list'); if($('#chat-msg-list').children().length>100) $('#chat-msg-list').children(':first').remove(); $("#chat-msg-list").scrollTop($("#chat-msg-list")[0].scrollHeight); } } function danmuListener(content_type, content){ if(content_type==='online'){ if(window.dom_changed===undefined){ $('#h5_player').prev().appendTo('#js-player-decorator'); window.dom_changed = true; } change_online(content); }else if(content_type==='msg'){ var content_obj = JSON.parse(content); emit_danmu(content_obj); append_danmu(content_obj); } } function init_danmaku() { window.df_danmaku = new Danmaku(); df_danmaku.init({ container: $('#js-player-decorator')[0], video: $("#h5_player")[0], engine:'canvas' }); $('canvas')[0].style.position = 'absolute'; // send danmu function send_danmu(){ var msg = $("#df-danmu-textbox").val(); var xhr = new XMLHttpRequest(); // xhr.setRequestHeader('X-Cookie', document.cookie); xhr.open('POST', 'http://live.bilibili.com/msg/send'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send($.param({ color: 16777215, fontsize: 25, mode: 1, msg: msg, rnd: Math.floor(Date.now() / 1000), roomid: room_id })); } $("#danmu-textbox").off('keypress'); $("#danmu-textbox").off('keyup'); $("#danmu-textbox").off('keydown'); $("#danmu-send-btn").off('click'); $("#danmu-textbox")[0].id = 'df-danmu-textbox'; $("#danmu-send-btn")[0].id = 'df-danmu-send-btn'; $("#df-danmu-textbox").on('keyup', function (e) { if(e.keyCode == 13){ send_danmu(); $("#df-danmu-textbox").val(''); e.preventDefault(); return false; } return true; }); $("#df-danmu-send-btn").on('click', function (e) { e.preventDefault(); send_danmu(); $("#df-danmu-textbox").val(''); }); } function set_danmu_control(){ if(location.pathname==='/'){ return; } var control_btn = $(""); control_btn.css('border-radius', '5px'); control_btn.css('font-size', '12px'); control_btn.height('21px'); $('.room-info.tag-ctnr.v-top').children().remove(); control_btn.appendTo('.room-info.tag-ctnr.v-top'); control_btn.on('click', function () { if(control_btn.text()=='打开弹幕'){ control_btn.text('关闭弹幕'); $('canvas')[0].style.display = 'block'; }else{ $('canvas')[0].style.display = 'none'; control_btn.text('打开弹幕'); } }); }