Greasy Fork is available in English.

BiliBili HTML5 Live

screw flash!

Tính đến 2017-06-12 16:37:14 UTC. Xem phiên bản mới nhất.

// ==UserScript==
// @name      BiliBili HTML5 Live
// @version   170613
// @description screw flash!
// @author    esterTion
// @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/esterTion/live_html5_lib/067dad61e9de705e7171c332a75627328cabc3ae/google-style-loading.min.js
// @require   https://cdn.rawgit.com/esterTion/live_html5_lib/0fa1c6dd49c3bde10abbd3449b3107da80f62dfa/live_html5_pack.min.js
// @require   https://cdn.rawgit.com/esterTion/live_html5_lib/8898bf69cca9437efb8569ffff6919040eedc7dc/flv.min.js
// @run-at    document-end
// @namespace https://greasyfork.org/users/24167
// ==/UserScript==
/*
Original script: https://greasyfork.org/zh-CN/scripts/27239-bilibili-%E7%9B%B4%E6%92%AD-html5-%E6%92%AD%E6%94%BE%E5%99%A8
flv.js: https://github.com/bilibili/flv.js
ABPlayer-bilibili-ver: https://github.com/Zhuogu/ABPlayerHTML5-bilibili-ver
*/
if(location.protocol=='https:'){
	location.href=location.href.replace('https:','http:');
}

var updateCSS=false,css,cssVer=170613;
if(!localStorage.ABPlayer_css)
	updateCSS=true
else{
	css=JSON.parse(localStorage.ABPlayer_css);
	if(css.ver!=cssVer)
	updateCSS=true
}
while(updateCSS){
	var xhr=new XMLHttpRequest();
	xhr.open('GET','https://cdn.rawgit.com/esterTion/live_html5_lib/0d3f2ba947263877164b51855f334f6685f2ef87/ABPlayer.min.css',false);
	xhr.send();
	if(xhr.status==200 || xhr.status==304)
		updateCSS=false;
	css={
		ver:cssVer,
		data:xhr.response
	}
	localStorage.ABPlayer_css=JSON.stringify(css);
}
var style = document.createElement('style');
style.id='ABPlayer';
style.textContent=css.data;
document.head.appendChild(style);

var playerSettings = localStorage.getItem('LIVE_PLAYER_STATUS');
if(playerSettings != null){
	var match = playerSettings.match('"type":"html5"');
	localStorage.LIVE_PLAYER_STATUS = playerSettings.replace('"type":"html5"','"type":"flash"');
	if(match!=null){
		window.addEventListener('load',function(){
			EmbedPlayer.loader();
		})
	}
}
var room_id,
player={
	flv:null,
	hls:null
},
flvplayer,
abpinst=null,
tryCount = 1,
i_close_it_myself = false,
df_domain = 'broadcastlv.chat.bilibili.com',
//df_domain = 'tx.biliplus.com',
df_portobj = {
	'ws': 7170,
	'wss': 7172
},
reconnecting=false,
inited=false,
living=true,
rounding=false,
restorePlayer=function(){
	living=false;
	rounding=true;
	if(abpinst!=null){
		destroyPlayer();
		abpinst.playerUnit.style.display='none';
		document.body.appendChild(abpinst.playerUnit);
	}
	unsafeWindow.EmbedPlayer.loader();
	var pop=unsafeWindow.livePopup({content:"轮播状态,正在切回官方播放器",width:"320px",type:'info'})
	setTimeout(function(){pop.close();},3e3);
	setTimeout(function(){
		var playerSettings = localStorage.getItem('LIVE_PLAYER_STATUS');
		if(playerSettings != null){
			var match = playerSettings.match('"type":"html5"');
			localStorage.LIVE_PLAYER_STATUS = playerSettings.replace('"type":"html5"','"type":"flash"');
			console.log('默认播放器设置已重置');
		}
	},5e3);
},
init=function(room_id){
	if(inited)
		return;
	inited=true;
	setInterval(function(){
		if(living && document.querySelector('#player_object')!=null){
			document.querySelector('#player_object').remove();
		}
	},500);
	get_url_and_replace_player(room_id);
	init_danmaku();
	click_list();
	function check(a){return(a<10)?('0'+a):a}
	var addTimeObserver = new MutationObserver(function(records){
		var now = new Date;
		now = check(now.getMonth()+1) +'-'+ check(now.getDate()) +' '+ check(now.getHours()) +':'+ check(now.getMinutes()) +':'+ check(now.getSeconds());
		records.forEach(function(i){
			Array.from(i.addedNodes).forEach(function(i){
				i.setAttribute('title',now);
			})
		})
	});
	addTimeObserver.observe(document.getElementById('chat-msg-list'), {
		childList:true,
		attributes:false
	})
},
hlsSpeedMonitor=(function(){
	var prevBytes=0,isLoading=false,loadingFrag=null;
return {
	reset:function(){
		prevBytes=0;
	},
	interval:setInterval(function(){
		if(player.hls==null){
			prevBytes=0;
			return;
		}
		var loadedBytes=prevBytes;
		prevBytes=0;
		if(isLoading){
			loadedBytes+=loadingFrag.loaded;
			prevBytes=-loadingFrag.loaded;
		}
		player.hls.speed=loadedBytes/1024;
	},1e3),
	loadStart:function(e,d){
		isLoading=true;
		loadingFrag=d.frag;
	},
	loadEnd:  function(e,d){
		if(abpinst.video.readyState==1){
			console.log('playback jumped from '+abpinst.video.currentTime.toFixed(2)+' to '+d.frag.start.toFixed(2));
			abpinst.video.currentTime=d.frag.start;
		}
		isLoading=false;
		loadingFrag=null;
		prevBytes+=d.stats.loaded;
	},
};})(),
destroyPlayer=function(){
	if(player.hls!=null){
		player.hls.destroy();
		player.hls=null;
	}
	if(player.flv!=null){
		player.flv.unload();
		player.flv.destroy();
		player.flv=null;
	}
	hlsSpeedMonitor.reset();
},
createPlayer={
	flv:function(url){
		destroyPlayer();
		var video=document.querySelector('.ABP-Unit video');
		player.flv = flvjs.createPlayer({
			type: 'flv',
			isLive: true,
			url: url
		});
		player.flv.attachMediaElement(video);
		player.flv.load();
		flvplayer=player.flv;
		video.currentTime=0;
		if(abpinst!=null){
			player.flv.on('error',function(){setTimeout(function(){
				abpinst.createPopup('直播流意外中断',3e3);
				changeSrc('',player.hls==null?'flv':'hls');
			},1e3);});
			video.dispatchEvent(new CustomEvent('autoplay'));
			var div=abpinst.playerUnit.querySelector('#info-box');
			if(div.style.opacity==0){
				div.style.opacity=1;
			}
			div.childNodes[0].childNodes[0].innerHTML='正在切换';
			dots.runTimer();
		}
	},
	hls:function(url){
		destroyPlayer();
		var video=document.querySelector('.ABP-Unit video');
		player.hls = new Hls();
		player.hls.loadSource(url);
		player.hls.attachMedia(video);
		player.hls.on(Hls.Events.FRAG_LOADING,hlsSpeedMonitor.loadStart);
		player.hls.on(Hls.Events.FRAG_LOADED,hlsSpeedMonitor.loadEnd);
		video.currentTime=0;
		video.dispatchEvent(new CustomEvent('autoplay'));
		var div=abpinst.playerUnit.querySelector('#info-box');
		if(div.style.opacity==0){
			div.style.opacity=1;
		}
		div.childNodes[0].childNodes[0].innerHTML='正在切换';
		dots.runTimer();
	}
},
tryInterval,
blockingGift=false,
blockGiftClickListener=function(){
	if(this.classList.contains('on')){
		blockingGift=false;
		this.className = 'btn block-gift';
	}else{
		blockingGift=true;
		this.className = 'btn block-gift on';
	}
};
window.changeSrc=function(b,a){
	switch(a){
		case 'flv':
			$.ajax({
				url: '//live.bilibili.com/api/playurl?otype=json&cid=' + room_id + '&_='+Date.now(),
				type: 'GET',
				dataType: 'json',
				success: function (data) {
					createPlayer.flv(data.durl[0].url);
				}
			});
		break;
		case 'hls':
			$.ajax({
				url: '//live.bilibili.com/api/playurl?platform=h5&otype=json&cid=' + room_id + '&_='+Date.now(),
				type: 'GET',
				dataType: 'json',
				success: function (data) {
					createPlayer.hls(data.data);
				}
			});
		break;
	}
}
unsafeWindow.player = player;
unsafeWindow.addEventListener('load',function(){

if(unsafeWindow.ROOMID){
	room_id=unsafeWindow.ROOMID;
	setInterval(function(){init(room_id);},1e3);
	var style=document.createElement('style');
	style.innerHTML=`
.treasure-box-ctnr{bottom:115px}
.block-gift{width:20px;height:20px;position:relative;--color:#999999;background:none !important}
.block-gift:active{--color:#29ABE1}
.block-gift.on{--color:#29ABE1}
.block-gift.on:active{--color:#999999}
.block-gift .block-gift-inner{width:14px;height:14px;position:absolute;top:3px;left:3px}
.block-gift .block-gift-inner g{fill:var(--color)}
.block-gift .block-gift-border{width:20px;height:20px;position:absolute}
.block-gift .block-gift-border g{stroke:var(--color);fill:transparent}
`;
	document.head.appendChild(style);
	var btns = document.querySelector('.chat-ctrl-btns .btns'),blockGift=document.createElement('div');
	btns.appendChild(blockGift);
	blockGift.className='btn block-gift';
	blockGift.setAttribute('title','屏蔽礼物信息');
	blockGift.innerHTML=`
<!-- Source: http://www.iconninja.com/giftbox-outline-icon-849682 -->
<svg class="block-gift-inner" width="935.375px" height="935.375px" viewBox="0 0 935.375 935.375">
<g><path d="M362.189,34.821C325.89,8.479,287.582-3.011,248.316,0.67c-33.322,3.124-60.324,14.123-80.258,32.69 c-19.385,18.057-30.58,42.083-32.374,69.48c-2.367,36.164,12.378,75.592,40.456,108.175c0.492,0.571,0.996,1.132,1.496,1.698 H38.074v265.732h33.219v456.929h792.789V478.447h33.219V212.715H757.736c0.5-0.565,1.004-1.127,1.496-1.698 c28.078-32.583,42.824-72.012,40.457-108.175c-1.795-27.397-12.988-51.425-32.375-69.48 c-19.934-18.568-46.936-29.566-80.258-32.69c-39.266-3.68-77.574,7.809-113.873,34.15c-26.717,19.388-52.33,46.951-76.129,81.925 c-11.295,16.599-21.104,33.273-29.369,48.659c-8.264-15.386-18.072-32.06-29.368-48.659 C414.52,81.771,388.906,54.208,362.189,34.821z M106.074,300.771v-20.056h723.227v20.057v109.674h-33.219h-213.57h-229.65H139.293 h-33.219V300.771z M420.862,478.447h93.65v136.35l-46.825-21.688l-46.825,21.688V478.447z M227.654,166.627 c-16.294-18.909-25.309-41.093-24.114-59.343c0.649-9.924,4.306-18.055,10.867-24.167c8.567-7.98,22.488-13.079,40.257-14.745 c22.491-2.109,44.457,4.815,67.152,21.167c20.247,14.587,40.299,36.274,59.603,64.458c13.919,20.324,25.4,41.105,34.167,58.717 h-110.44C274.073,204.938,246.652,188.675,227.654,166.627z M796.08,867.375H139.293V478.447h213.569v242.787l114.825-53.187 l114.825,53.186V478.447h213.57v388.928H796.08z M553.273,155c19.369-28.464,39.504-50.382,59.85-65.145 c22.846-16.578,44.951-23.606,67.588-21.483c17.77,1.666,31.689,6.765,40.258,14.745c6.561,6.112,10.217,14.242,10.867,24.167 c1.193,18.25-7.82,40.435-24.115,59.343c-18.998,22.047-46.42,38.311-77.49,46.087H519.762 C528.361,195.442,539.605,175.085,553.273,155z"/></g>
</svg>

<svg class="block-gift-border" width="20px" height="20px">
	<g>
		<circle cx=10 cy=10 r=9 />
		<path d="M3.5355,3.5355L16.4645,16.4645" style="stroke-width:2px" />
	</g>
</svg>
`;
	blockGift.addEventListener('click',blockGiftClickListener);
}else{
tryInterval = setInterval(function () {
	if (tryCount > 20) {
		console.log('quiting');
		clearInterval(tryInterval);
		return;
	}
	console.log('finding', tryCount++);
	var link = document.querySelector('#player_object [name="flashvars"]');
	if(link==null)
		return;
	room_id = link.value.match(/cid=(.*?)&/)[1];
	clearInterval(tryInterval);
	console.log('found');
	init(room_id);
}, 1000);
}

})

function get_url_and_replace_player(room_id) {
	$.ajax({
		url: '//live.bilibili.com/api/player?id=cid:' + room_id,
		type: 'GET',
		success: function (data) {
			try{
			var parser=new DOMParser();
			var xml=parser.parseFromString('<player>'+data+'</player>','text/xml');
			var limit = xml.getElementsByTagName('msg_length')[0].textContent,
			state = xml.getElementsByTagName('state')[0].textContent;
			$('#df-danmu-textbox,#danmu-textbox') [0].setAttribute('maxlength', (limit|0) || 20);
			$('.danmu-length-count>:last-child')[0].textContent=30
			if(state == 'ROUND'){
				restorePlayer();
			}else if(state == 'PREPARING'){
				if(abpinst!=null){
					dots.stopTimer();
					abpinst.playerUnit.querySelector('#info-box .text-wrapper>div').innerHTML='未在直播';
					destroyPlayer();
				}
				living=false;
			}
			
			df_domain = xml.getElementsByTagName('dm_server')[0].textContent;
			df_portobj.ws = xml.getElementsByTagName('dm_ws_port')[0].textContent|0;
			df_portobj.wss = xml.getElementsByTagName('dm_wss_port')[0].textContent|0;
			unsafeWindow.df_danmu_ws = new DanmuSocket(parseInt(room_id), df_domain, df_portobj);
			unsafeWindow.df_danmu_ws.setListener(danmuListener);
			}catch(e){
				console.error('error processing player data: ',e.message);
			}
		}
	});
	var api_url = '//live.bilibili.com/api/playurl?otype=json&cid=' + room_id;
	$.ajax({
		url: api_url,
		type: 'GET',
		dataType: 'json',
		success: function (data) {
			if(!living)return;
			replace_player(data.durl[0].url);
			if (unsafeWindow.df_danmu_ws !== undefined) {
				i_close_it_myself = true;
				unsafeWindow.df_danmu_ws.close();
				unsafeWindow.df_danmu_ws = undefined;
			}
		}
	});
	createABP();
}
function createABP(){
	var video=document.createElement('video');
	document.querySelector('#js-player-decorator').appendChild(video);
	var abpOptions = {
		scale: 0.8,
		opacity: 0.7,
		speed: 1,
		useCSS: false,
		autoOpacity: false
	};
	try {
		var settings = localStorage.html5Settings || '{}';
		settings = JSON.parse(settings);
		abpOptions.scale = settings.scale || 1;
		abpOptions.opacity = settings.opacity || 1;
		abpOptions.speed = settings.speed || 1;
		abpOptions.useCSS = settings.useCSS || false;
		abpOptions.autoOpacity = settings.autoOpacity || false;
	} catch (e) {
	}
	abpinst = ABP.create(document.getElementById('js-player-decorator'), {
		src: {
			playlist: [
				{
					video: video
				}
			]
		},
		width: '100%',
		height: '100%',
		config: abpOptions
	});
	dots.runTimer();
}
function replace_player(flv_url) {
	var w = $('#js-player-decorator').width();
	var h = $('#js-player-decorator').height();
	flvjs.LoggingControl.enableVerbose = false;
	flvjs.LoggingControl.enableInfo = false;
	flvjs.LoggingControl.enableDebug = false;
	createPlayer.flv(flv_url);
	abpinst.playerUnit.querySelector('#info-box .text-wrapper>div').innerHTML='正在加载视频信息';
	unsafeWindow.addEventListener('unload', destroyPlayer);
	for(var i=0,div,types=[['flv',''],['hls','']];i<2;i++){
		div=document.createElement('div');
		div.setAttribute('changeto',JSON.stringify(types[i]));
		//div.setAttribute('name',availableSrc[i][0]);
		if( i==0 )
			div.className='on';
		div.appendChild(document.createTextNode(types[i][0]));
		abpinst.playerUnit.querySelector('.BiliPlus-Scale-Menu .Video-Defination').appendChild(div);
	}
	unsafeWindow.abpinst = abpinst;
	if (settings.commentVisible === false) {
		abpinst.btnDm.click();
	}
	abpinst.video.addEventListener('ended',function(){
		abpinst.createPopup('直播流意外结束',3e3);
		changeSrc('',player.hls==null?'flv':'hls');
	})
}
function click_list() {
	if (unsafeWindow.location.pathname === '/') {
		$($('[role="list"]') [0]).children().on('click', function () {
			var room_id = $(this).attr('data-cid');
			get_url_and_replace_player(room_id);
		});
	}
}

var rawHeaderLen = 16;
var packetOffset = 0;
var headerOffset = 4;
var verOffset = 6;
var opOffset = 8;
var seqOffset = 12;
var pako = unsafeWindow.pako;
var textDecoder = getDecoder(true);
var textEncoder = getEncoder();
var heartbeatInterval;
function getDecoder(isUseful) {
	if (unsafeWindow['TextDecoder'] && isUseful) {
		return new unsafeWindow['TextDecoder']();
	} else {
		return {
			decode: (buf) => {
				return decodeURIComponent(unsafeWindow.escape(String.fromCharCode.apply(null, new Uint8Array(buf))));
			}
		};
	}
}
function getEncoder() {
	if (unsafeWindow['TextEncoder']) {
		return new unsafeWindow['TextEncoder']();
	} else {
		return {
			encode: (str) => {
				var buf = new ArrayBuffer(str.length);
				var bufView = new Uint8Array(buf);
				for(var 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;
}
function DanmuSocket(roomid, domain, portobj){
	var ws = unsafeWindow.location.protocol.indexOf('https') > - 1 ? 'wss' : 'ws';
	var 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;
}
DanmuSocket.prototype.firstConnection=function() {
	reconnecting=false;
	console.log('Danmu WebSocket Server Connected.');
	console.log('Handshaking...');
	var token = JSON.stringify({
		'uid': unsafeWindow.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));
};
DanmuSocket.prototype.heartBeat=function() {
	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);
};
DanmuSocket.prototype.closeHeartBeat=function() {
	clearInterval(this.heartBeating);
};
DanmuSocket.prototype.send=function(data) {
	this.connection.send(data);
};
DanmuSocket.prototype.close=function() {
	this.connection.close();
};
DanmuSocket.prototype.setListener=function(listener) {
	this._listener = listener;
};
function onMessage(evt) {
	var data = evt.data;
	var dataView = new DataView(data, 0);
	var packetLen = dataView.getUint32(packetOffset);
	if (dataView.byteLength >= packetLen) {
		var headerLen = dataView.getInt16(headerOffset);
		var ver = dataView.getInt16(verOffset);
		var op = dataView.getUint32(opOffset);
		var seq = dataView.getUint32(seqOffset);
		switch (op) {
			case 8:
				this.heartBeat();
				heartbeatInterval = setInterval(this.heartBeat.bind(this), 30 * 1000);
				break;
			case 3:
				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.getUint32(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) {
		setTimeout(reConnect, 1000);
	}
	i_close_it_myself = false;
}
function onError() {
	console.log('Client Error.');
}
function reConnect() {
	if(reconnecting)return;
	reconnecting=true;
	console.log('reconnecting socket...');
	unsafeWindow.df_danmu_ws = new DanmuSocket(parseInt(room_id), df_domain, df_portobj);
	unsafeWindow.df_danmu_ws.setListener(danmuListener);
}

function change_online(online) {
	unsafeWindow.PlayerSetOnline(online);
}
function emit_danmu(data) {
	if (data.cmd === 'DANMU_MSG') {
		abpinst.cmManager.send({
			text: data.info[1],
			mode: data.info[0][1],
			color: data.info[0][3],
			hash: data.info[0][7],
			size: data.info[0][2],
			border: (data.info[2][0]==unsafeWindow.UID)
		});
	}
}
oriServerCb=unsafeWindow.server_callback;
unsafeWindow.server_callback=function(data){
	if([
		'SEND_GIFT',
		'SYS_MSG',
		'SYS_GIFT',
		'WELCOME',
		'SPECIAL_GIFT',
		'SEND_TOP'
	].indexOf(data.cmd)!=-1 && blockingGift)
		return;
	oriServerCb(data);
}
function append_danmu(data) {
	switch (data.cmd) {
		case 'ROUND':
			restorePlayer();
			return;
		case 'CLOSE':
		case 'PREPARING':
		case 'END':
			if(abpinst!=null){
				dots.stopTimer();
				abpinst.playerUnit.querySelector('#info-box .text-wrapper>div').innerHTML='未在直播';
				destroyPlayer();
			}
			living=false;
			unsafeWindow.server_callback(data);
			break;
		case 'LIVE':
			if(rounding){
				EmbedPlayer.loader();
				document.getElementById('js-player-decorator').appendChild(abpinst.playerUnit);
				abpinst.playerUnit.style.display='';
				rounding=false;
				living=true;
			}
			changeSrc(0,'flv');
			break;
	}
	if(!living)return;
	unsafeWindow.server_callback(data);
}
function danmuListener(content_type, content) {
	if (content_type === 'online') {
		if (unsafeWindow.dom_changed === undefined) {
			$('#h5_player').prev().appendTo('#js-player-decorator');
			unsafeWindow.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() {
	// 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('');
	});
}