// ==UserScript==
// @name csscloud flash 播放器替换
// @namespace https://home.asec01.net/
// @version 0.4.2
// @description 将 csscloud 的 flash 播放器换为 DPlayer
// @author Zhe Zhang
// @license MIT
// @supportURL https://github.com/zzzz0317/csscloud-flash-player-replacer/
// @icon https://github.com/zzzz0317/csscloud-flash-player-replacer/raw/master/favicon_csscloud.ico
// @match http://view.csslcloud.net/api/view/*
// @match https://view.csslcloud.net/api/view/*
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/flv.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/DPlayer.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/base64.min.js
// ==/UserScript==
var jq = jQuery.noConflict();
(function () {
var zzValue = {
"name": "ZZ 的播放器替换脚本",
"projectLink": "https://github.com/zzzz0317/csscloud-flash-player-replacer/",
"mainMsg": "欢迎使用 ZZ 的 csscloud 播放器替换脚本",
"mainMsgShowTime": 5000
};
var playerSettings = {
'showNameInDanmaku': false,
'preventLivePause': false,
'preventRetryError': false
};
function readPlayerSettings() {
var v = GM_getValue('playerSettings');
if (v != undefined) {
playerSettings = v;
} else {
savePlayerSettings();
}
}
function savePlayerSettings() {
GM_setValue('playerSettings', playerSettings);
}
function setPlayerSettings(item, value) {
playerSettings[item] = value;
savePlayerSettings();
}
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return false;
}
var dp;
var dpContextMenu = [
{
text: getZZValue("name"),
link: getZZValue("projectLink"),
},
{
text: '弹幕发送者显示开关',
click: (player) => {
setPlayerSettings("showNameInDanmaku", !playerSettings.showNameInDanmaku);
if (playerSettings.showNameInDanmaku) {
player.notice("显示弹幕发送者");
} else {
player.notice("不显示弹幕发送者");
}
},
},
{
text: '阻止暂停开关',
click: (player) => {
setPlayerSettings("preventLivePause", !playerSettings.preventLivePause);
if (playerSettings.preventLivePause) {
player.notice("阻止暂停功能已打开");
} else {
player.notice("阻止暂停功能已关闭");
}
},
},
{
text: '阻止失败刷新开关',
click: (player) => {
setPlayerSettings("preventRetryError", !playerSettings.preventRetryError);
if (playerSettings.preventRetryError) {
player.notice("阻止失败刷新功能已打开");
} else {
player.notice("阻止失败刷新功能已关闭");
}
},
},
{
text: '复制视频链接到剪贴板',
click: (player) => {
zzlog("视频链接:\n" + player.options.video.url);
GM_setClipboard(player.options.video.url);
},
},
{
text: '输出播放器信息到控制台',
click: (player) => {
console.log(player);
},
},
{
text: '--------------------'
},
];
function playLive(u) {
zzlog("监听聊天框");
var targetNode = document.getElementById('chat-list');
var config = {attributes: true, childList: true, subtree: true};
const mutationCallback = (mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type == "childList") {
var nodeElem = mutation.addedNodes[1];
console.log(nodeElem);
var displayContent = nodeElem.getElementsByClassName("peo-chat")[0].getElementsByClassName("chat-content")[0].innerHTML;
if (playerSettings.showNameInDanmaku) {
var displayName = nodeElem.getElementsByClassName("peo-names")[0].innerText;
addDanmaku(displayName + " : " + displayContent);
} else {
addDanmaku(displayContent);
}
}
}
};
var observer = new MutationObserver(mutationCallback);
observer.observe(targetNode, config);
zzlog("playLive播放链接:\n" + u);
dp = new DPlayer({
container: document.getElementById('videoElement'),
autoplay: true,
live: true,
screenshot: true,
volume: 1,
danmaku: true,
contextmenu: dpContextMenu,
apiBackend: {
read: function (endpoint, callback) {
endpoint.success();
},
send: function (endpoint, danmakuData, callback) {
observer.disconnect();
console.log(endpoint);
zzlog("发送弹幕: " + endpoint.data.text);
jq("#chatContent").val(endpoint.data.text);
sendChatMsg();
endpoint.success();
setTimeout(function () {
observer.observe(targetNode, config);
}, 200);
},
},
video: {
url: u,
},
});
dp.on('pause', function () {
if (playerSettings.preventLivePause) {
dp.play();
dp.notice("直播,请不要暂停", 1000);
} else {
dp.notice("直播,建议不要暂停", 1000);
}
});
dp.plugins.flvjs.on(flvjs.Events.ERROR, (errType, errDetail) => {
if (playerSettings.preventRetryError) {
dp.notice("拉流出错,播放停止", 1000);
} else {
dp.notice("拉流出错,刷新页面中......", 1000);
location.reload();
}
});
setTimeout(function () {
var catchFrame = false;
function readLoop() {
var maxDelay = 8;
var catchFrameSpeed = 1.5;
var currentTime = dp.video.currentTime;
var bufferedEnd = dp.video.buffered.end(0);
var bufferedLength = dp.video.buffered.length;
var delayTime = bufferedEnd - currentTime;
zzlog("延迟定时检测\n" + "dp.video.currentTime: " + currentTime +
"\ndp.video.buffered.end(0): " + bufferedEnd +
"\ndp.video.buffered.length: " + bufferedLength);
if (bufferedLength > 0 && delayTime > maxDelay) {
zzlog("延迟" + delayTime + "秒,追帧");
dp.speed(catchFrameSpeed);
catchFrame = true;
setTimeout(function () {
dp.speed(1);
catchFrame = false;
zzlog("追帧完成\n" + "dp.video.currentTime: " + dp.video.currentTime +
"\ndp.video.buffered.end(0): " + dp.video.buffered.end(0) +
"\ndp.video.buffered.length: " + dp.video.buffered.length);
}, (delayTime - 3) * 1000);
// dp.video.currentTime = bufferedEnd - 3;
}
}
setInterval(function () {
if (catchFrame) {
zzlog("正在追帧,取消本次追帧检测");
} else {
readLoop();
}
}, 10000);
}, 10000);
zzWelcomeDanmaku();
}
var danmakuArray = [];
function playLink(u) {
zzlog("playLink播放链接:\n" + u);
dp = new DPlayer({
container: document.getElementById('videoElement'),
autoplay: true,
live: false,
screenshot: true,
volume: 1,
danmaku: true,
contextmenu: dpContextMenu,
apiBackend: {
read: function (endpoint, callback) {
endpoint.success();
},
send: function (endpoint, danmakuData, callback) {
endpoint.success();
},
},
video: {
url: u,
},
});
function readLoop() {
var currentTime = dp.video.currentTime;
var cTime = parseInt(currentTime);
danmakuArray.forEach(function (item) {
//console.log(item);
if (item.time == cTime) {
if (playerSettings.showNameInDanmaku) {
addDanmaku(item.userName + " : " + showEm(item.content));
} else {
addDanmaku(showEm(item.content));
}
var realTimeMsg = {
userid: item.userId,
username: item.userName,
msg: item.content,
time: item.time
};
on_cc_live_chat_msg(realTimeMsg);
}
})
}
var readInter;
dp.on('pause', function () {
zzlog('播放暂停');
clearInterval(readInter);
});
dp.on('play', function () {
zzlog('播放');
readInter = setInterval(function () {
readLoop()
}, 1000);
});
zzWelcomeDanmaku();
}
function addDanmaku(t) {
const danmaku = {
text: t,
color: '#ffffff',
type: 'right',
};
dp.danmaku.draw(danmaku);
}
function zzlog(t) {
console.log("%cZZ csscloud userscript\n%c" + t, "font-weight:bold", "");
}
function zzWelcome() {
console.log("\n" +
"%cZZ Injected\n" +
"%c\n欢迎使用 ZZ 的 csscloud 播放器替换脚本\n" +
"项目主页:https://github.com/zzzz0317/csscloud-flash-player-replacer/\n" +
"作者主页:https://home.asec01.net/\n", "font-size:20pt", "");
}
function zzWelcomeDanmaku() {
dp.notice(getZZValue("mainMsg"), getZZValue("mainMsgShowTime"));
}
function refreshZZValue() {
var v = GM_getValue('zzValue');
if (v != undefined) {
zzValue = v;
}
jq.ajax({
method: 'GET',
url: '//www.zhangzhe-tech.cn/copyright-files/csscloud-flash-player-replacer.json',
data: {
rand: Math.ceil(Math.random() * 1000)
},
success: function (data) {
console.log(data);
zzValue = data.data;
GM_setValue('zzValue', zzValue);
},
error: function (jqXHR, textStatus, errorThrown) {
zzlog("refreshValueError: " + textStatus + " " + jqXHR.status + " " + errorThrown);
}
});
}
function getZZValue(item) {
var v = "";
try {
v = zzValue[item];
} catch (err) {
v = "Unknown value";
zzlog("getValueError: " + err.description);
}
return v;
}
'use strict';
zzWelcome();
zzlog("初始化");
var isHttps = 'https:' == document.location.protocol ? true : false;
var roomId = getQueryVariable("roomid");
var recordId = getQueryVariable("recordid");
var liveId = getQueryVariable("liveid");
var userId = getQueryVariable("userid");
zzlog("roomId: " + roomId);
zzlog("recordId: " + recordId);
zzlog("liveId: " + recordId);
zzlog("userId: " + userId);
zzlog("isHttps: " + isHttps);
readPlayerSettings();
jq(document).ready(function () {
zzlog("Dom加载完成");
var livePlayer = jq('#doc-main');
if (livePlayer.length == 1) {
// jq(livePlayer).html('<video id="videoElement" height="100%" width="100%" autoplay controls></video>');
jq(livePlayer).html('<div id="videoElement"></div>');
}
GM_addStyle(".videoElement { width: 100%; height: 100%; }");
GM_addStyle(".dplayer { width: 100%; height: 100%; }");
GM_addStyle(".video-middle { background-color: black; }");
if (recordId == false) {
if (roomId == false) {
zzlog("参数错误 - 未获取到roomid和recordId");
} else {
zzlog("直播模式");
playLive('//stream-ali1.csslcloud.net/src/' + roomId + '.flv');
}
} else {
zzlog("回放模式");
GM_addStyle("#doc-main { height: 100%; }");
var lmb = document.getElementsByClassName("l-m-b")[0];
lmb.style.display = "none";
jq.ajax({
method: 'GET',
url: '//view.csslcloud.net/api/vod/v2/play/h5',
data: {
recordid: recordId,
userid: userId
},
success: function (data) {
//console.log(data);
var linkObj = data["video"][0];
var link = "";
if (isHttps) {
link = linkObj["secureplayurl"];
} else {
link = linkObj["playurl"];
}
playLink(link);
}
});
jq.ajax({
method: 'GET',
url: '//view.csslcloud.net/api/view/replay/chatqa/info',
data: {
roomid: roomId,
liveid: liveId,
recordid: recordId,
userid: userId
},
success: function (data) {
// console.log(data);
danmakuArray = data.datas.meta.chatLog;
}
});
}
});
refreshZZValue();
})();