B 站的直播的 HTML5 播放器
当前为
// ==UserScript==
// @name bilibili 直播 HTML5 播放器
// @namespace https://www.kindjeff.com/
// @version 2017.2.21
// @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
// @run-at document-end
// ==/UserScript==
/***/
(function(t,i){typeof exports==="object"&&typeof module!=="undefined"?module.exports=i():typeof define==="function"&&define.amd?define(i):t.Danmaku=i()})(this,function(){"use strict";function t(){var t=9007199254740991;return[{range:0,time:-t,width:t,height:0},{range:t,time:t,width:0,height:0}]}var i={};function e(){i.ltr=t();i.rtl=t();i.top=t();i.bottom=t()}e();var s=function(t){var e=this;var s=this._hasMedia?this.media.currentTime:Date.now()/1e3;var n=this._hasMedia?this.media.playbackRate:1;function h(t,i){if(i.mode==="top"||i.mode==="bottom"){return s-t.time<e.duration}var h=e.width+t.width;var a=h*(s-t.time)*n/e.duration;if(t.width>a){return true}var r=e.duration+t.time-s;var o=e.duration*e.width/(e.width+i.width);return r>o}var a=i[t.mode];var r=0;var o=0;for(var d=1;d<a.length;d++){var u=a[d];var m=t.height;if(t.mode==="top"||t.mode==="bottom"){m+=u.height}if(u.range-u.height-a[r].range>=m){o=d;break}if(h(u,t)){r=d}}var l=a[r].range;var c={range:l+t.height,time:this._hasMedia?t.time:t._utc,width:t.width,height:t.height};a.splice(r+1,o-r-1,c);if(t.mode==="bottom"){return this.height-t.height-l%this.height}return l%(this.height-t.height)};var n=function(t){var i=document.createElement("div");if(t.html===true){i.innerHTML=t.text}else{i.textContent=t.text}i.style.cssText="position:absolute;";if(t.style){for(var e in t.style){i.style[e]=t.style[e]}}return i};var h=function(){var t=["oTransform","msTransform","mozTransform","webkitTransform","transform"];var i=document.createElement("div").style;for(var e=0;e<t.length;e++){if(t[e]in i){return t[e]}}return"transform"}();var a=function(){var t=Date.now()/1e3;var i=this._hasMedia?this.media.currentTime:t;var e=this._hasMedia?this.media.playbackRate:1;var a=null;var r=0;var o=0;for(o=0;o<this.runningList.length;o++){a=this.runningList[o];r=this._hasMedia?a.time:a._utc;if(i-r>this.duration){this.stage.removeChild(a.node);if(!this._hasMedia){a.node=null}this.runningList.splice(o,1)}}var d=[];var u=document.createDocumentFragment();while(this.position<this.comments.length){a=this.comments[this.position];r=this._hasMedia?a.time:a._utc;if(r>=i){break}a._utc=Date.now()/1e3;a.node=a.node||n(a);this.runningList.push(a);d.push(a);u.appendChild(a.node);++this.position}if(d.length){this.stage.appendChild(u)}for(o=0;o<d.length;o++){a=d[o];a.width=a.width||a.node.offsetWidth;a.height=a.height||a.node.offsetHeight}for(o=0;o<d.length;o++){a=d[o];a.y=s.call(this,a);if(a.mode==="top"||a.mode==="bottom"){a.x=this.width-a.width>>1;a.node.style[h]="translate("+a.x+"px,"+a.y+"px)"}}for(o=0;o<this.runningList.length;o++){a=this.runningList[o];if(a.mode==="top"||a.mode==="bottom"){continue}var m=this.width+a.width;var l=m*(t-a._utc)*e/this.duration;l|=0;if(a.mode==="ltr")a.x=l-a.width;if(a.mode==="rtl")a.x=this.width-l;a.node.style[h]="translate("+a.x+"px,"+a.y+"px)"}};var r=16;var o=16;function d(t){var i=window.getComputedStyle(t,null).getPropertyValue("font-size").match(/(.+)px/)[1]*1;if(t.tagName==="HTML"){o=i}else{r=i}}var u=Object.create(null);var m=function(t){if(u[t]){return u[t]}var i=12;var e=/^(\d+(?:\.\d+)?)(px|%|em|rem)(?:\s*\/\s*(\d+(?:\.\d+)?)(px|%|em|rem)?)?/;var s=t.match(e);if(s){var n=s[1]*1||10;var h=s[2];var a=s[3]*1||1.2;var d=s[4];if(h==="%")n*=r/100;if(h==="em")n*=r;if(h==="rem")n*=o;if(d==="px")i=a;if(d==="%")i=n*a/100;if(d==="em")i=n*a;if(d==="rem")i=o*a;if(d===undefined)i=n*a}u[t]=i;return i};var l=function(t){var i=document.createElement("canvas");var e=i.getContext("2d");var s=t.canvasStyle||{};s.font=s.font||"10px sans-serif";s.textBaseline=s.textBaseline||"bottom";var n=s.lineWidth*1;n=n>0&&n!==Infinity?Math.ceil(n):!!s.strokeStyle*1;e.font=s.font;t.width=t.width||Math.max(1,Math.ceil(e.measureText(t.text).width)+n*2);t.height=t.height||Math.ceil(m(s.font))+n*2;i.width=t.width;i.height=t.height;for(var h in s){e[h]=s[h]}var a=0;switch(s.textBaseline){case"top":case"hanging":a=n;break;case"middle":a=t.height>>1;break;default:a=t.height-n}if(s.strokeStyle){e.strokeText(t.text,n,a)}e.fillText(t.text,n,a);return i};var c=function(){this.stage.context.clearRect(0,0,this.width,this.height);var t=Date.now()/1e3;var i=this._hasMedia?this.media.currentTime:t;var e=this._hasMedia?this.media.playbackRate:1;var n=null;var h=0;var a=0;for(a=0;a<this.runningList.length;a++){n=this.runningList[a];h=this._hasMedia?n.time:n._utc;if(i-h>this.duration){n.canvas=null;this.runningList.splice(a,1)}}while(this.position<this.comments.length){n=this.comments[this.position];h=this._hasMedia?n.time:n._utc;if(h>=i){break}n._utc=Date.now()/1e3;n.canvas=l(n);n.y=s.call(this,n);if(n.mode==="top"||n.mode==="bottom"){n.x=this.width-n.width>>1}this.runningList.push(n);++this.position}for(a=0;a<this.runningList.length;a++){n=this.runningList[a];var r=this.width+n.width;var o=r*(t-n._utc)*e/this.duration;if(n.mode==="ltr")n.x=o-n.width+.5|0;if(n.mode==="rtl")n.x=this.width-o+.5|0;this.stage.context.drawImage(n.canvas,n.x,n.y)}};var f=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(t){return setTimeout(t,50/3)};var v=window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame||clearTimeout;var p=function(){if(!this.visible||!this.paused){return this}this.paused=false;if(this._hasMedia){for(var t=0;t<this.runningList.length;t++){var i=this.runningList[t];i._utc=Date.now()/1e3-(this.media.currentTime-i.time)}}var e=this;var s=this._useCanvas?c:a;function n(){s.call(e);e._requestID=f(n)}this._requestID=f(n);return this};var g=function(){if(!this.visible||this.paused){return this}this.paused=true;v(this._requestID);this._requestID=0;return this};var w=function(t,i,e){var s=0;var n=0;var h=t.length;while(n<h-1){s=n+h>>1;if(e>=t[s][i]){n=s}else{h=s}}if(t[n]&&e<t[n][i]){return n}return h};var _=function(){if(!this._hasMedia){return this}this.clear();e();var t=w(this.comments,"time",this.media.currentTime);this.position=Math.max(0,t-1);return this};var y=null;var x=null;var b=null;function M(){y=p.bind(this);x=g.bind(this);b=_.bind(this);this.media.addEventListener("play",y);this.media.addEventListener("pause",x);this.media.addEventListener("seeking",b)}function C(){this.media.removeEventListener("play",y);this.media.removeEventListener("pause",x);this.media.removeEventListener("seeking",b);y=null;x=null;b=null}var L=function(t){if(!/^(ltr|top|bottom)$/i.test(t)){return"rtl"}return t.toLowerCase()};var T=function(t){t.prototype.init=function(t){if(this._isInited){return this}if(!t||!t.container&&(!t.video||t.video&&!t.video.parentNode)){throw new Error("Danmaku requires container when initializing.")}this._hasInitContainer=!!t.container;this.container=t.container;this.visible=true;this.engine=(t.engine||"DOM").toLowerCase();this._useCanvas=this.engine==="canvas";this._requestID=0;this._speed=Math.max(0,t.speed)||144;this.duration=4;this.comments=JSON.parse(JSON.stringify(t.comments||[]));this.comments.sort(function(t,i){return t.time-i.time});for(var i=0;i<this.comments.length;i++){this.comments[i].mode=L(this.comments[i].mode)}this.runningList=[];this.position=0;this.paused=true;this.media=t.video||t.audio;this._hasMedia=!!this.media;this._hasVideo=!!t.video;if(this._hasVideo&&!this._hasInitContainer){var e=!this.media.paused;this.container=document.createElement("div");this.container.style.position=this.media.style.position;this.media.style.position="absolute";this.media.parentNode.insertBefore(this.container,this.media);this.container.appendChild(this.media);if(e&&this.media.paused){this.media.play()}}if(this._hasMedia){M.call(this)}if(this._useCanvas){this.stage=document.createElement("canvas");this.stage.context=this.stage.getContext("2d")}else{this.stage=document.createElement("div");this.stage.style.cssText="overflow:hidden;white-space:nowrap;transform:translateZ(0);"}this.stage.style.cssText+="position:relative;pointer-events:none;";this.resize();this.container.appendChild(this.stage);d(document.getElementsByTagName("html")[0]);d(this.container);if(!this._hasMedia||!this.media.paused){_.call(this);p.call(this)}this._isInited=true;return this}};var k=function(t){t.prototype.emit=function(t){if(!t||Object.prototype.toString.call(t)!=="[object Object]"){return this}var i=JSON.parse(JSON.stringify(t));i.text=(i.text||"").toString();i.mode=L(i.mode);i._utc=Date.now()/1e3;if(this._hasMedia){var e=0;if(i.time===undefined){i.time=this.media.currentTime;e=this.position}else{e=w(this.comments,"time",i.time)}this.comments.splice(e,0,i)}else{this.comments.push(i)}return this}};var D=function(t){t.prototype.clear=function(){if(this._useCanvas){this.stage.context.clearRect(0,0,this.width,this.height);for(var t=0;t<this.runningList.length;t++){this.runningList[t].canvas=null}}else{var i=this.stage.lastChild;while(i){this.stage.removeChild(i);i=this.stage.lastChild}}this.runningList=[];return this}};var I=function(t){t.prototype.destroy=function(){if(!this._isInited){return this}g.call(this);this.clear();if(this._hasMedia){C.call(this)}e();if(this._hasVideo&&!this._hasInitContainer){var t=!this.media.paused;this.media.style.position=this.container.style.position;this.container.parentNode.appendChild(this.media);this.container.parentNode.removeChild(this.container);if(t&&this.media.paused){this.media.play()}}for(var i in this){if(Object.prototype.hasOwnProperty.call(this,i)){this[i]=null}}return this}};var E=function(t){t.prototype.show=function(){if(this.visible){return this}this.visible=true;if(this._hasMedia&&this.media.paused){return this}_.call(this);p.call(this);return this}};var N=function(t){t.prototype.hide=function(){if(!this.visible){return this}g.call(this);this.clear();this.visible=false;return this}};var O=function(t){t.prototype.resize=function(){if(this._hasInitContainer){this.width=this.container.offsetWidth;this.height=this.container.offsetHeight}if(this._hasVideo&&(!this._hasInitContainer||!this.width||!this.height)){this.width=this.media.clientWidth;this.height=this.media.clientHeight}if(this._useCanvas){this.stage.width=this.width;this.stage.height=this.height}else{this.stage.style.width=this.width+"px";this.stage.style.height=this.height+"px"}this.duration=this.width/this._speed;return this}};var S=function(t){Object.defineProperty(t.prototype,"speed",{get:function(){return this._speed},set:function(t){if(typeof t!=="number"||isNaN(t)||!isFinite(t)||t<=0){return this._speed}this._speed=t;if(this.width){this.duration=this.width/t}return t}})};function q(t){this._isInited=false;t&&this.init(t)}T(q);k(q);D(q);I(q);E(q);N(q);O(q);S(q);return q});
/***/
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<msg.byteLength; offset+=packetLen) {
packetLen = packetView.getInt32(offset);
headerLen = packetView.getInt16(offset+headerOffset);
msgBody = textDecoder.decode(msg.slice(offset+headerLen, offset+packetLen));
if (!msgBody) {
textDecoder = getDecoder(false);
msgBody = textDecoder.decode(msg.slice(offset+headerLen, offset+packetLen));
}
if (this._listener) this._listener('msg', msgBody);
}
break;
}
}
function onClose () {
if (heartbeatInterval) clearInterval(heartbeatInterval);
if(! i_close_it_myself){
var delay = Math.floor(Math.random() * (6 - 3) + 3);
setTimeout(this.firstConnection.bind(this), delay * 1000);
console.log(delay);
}
i_close_it_myself = false;
}
function onError () {
console.log("Client Error.");
}
/*******************/
function change_online(online) {
$('span.v-bottom').text(online + ' 人');
}
function emit_danmu(data) {
if(data.cmd==='DANMU_MSG'){
var msg = data.info[1];
window.df_danmaku.emit({
text: msg,
canvasStyle: {
font: data.info[0][2]+'px sans-serif',
textAlign: 'start',
textBaseline: 'bottom',
direction: 'inherit',
fillStyle: '#fff',
strokeStyle: '#000',
lineWidth: 1.2,
shadowBlur: 0,
shadowColor: '#000',
shadowOffsetX: 0,
shadowOffsetY: 0,
filter: 'none',
globalAlpha: 1.0
}
})
}else if(data.cmd==='WELCOME'){
}else if(data.cmd==='SEND_GIFT'){
}
}
function append_danmu(data) {
if(data.cmd==='DANMU_MSG'){
var u_name = data.info[2][1];
var uid = data.info[2][0];
var lv = data.info[4][0];
var rank = data.info[4][3]; if(typeof(rank)=='string'&&rank.indexOf('>')!==-1) {rank.replace('>', '>')}
var msg = data.info[1];
// console.log(u_name,uid,lv,rank,msg);
var comment_div = '<div class="msg-item-ctnr"><div class="chat-msg " data-uname="'+u_name+'" data-uid="'+uid+'"><div class="user-level-icon lv-'+lv+'"> UL '+lv+' <div class="user-level-info"><p>用户等级:'+lv+'</p><p><a href="http://live.bilibili.com/rank" target="_blank">排名:'+rank+'</a></p></div></div><span class="user-name color">'+u_name+' : </span><span class="msg-content">'+msg+'</span></div></div>';
$(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 = $("<button>关闭弹幕</button>");
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('打开弹幕');
}
});
}