csscloud flash 播放器替换

将 csscloud 的 flash 播放器换为 DPlayer

当前为 2020-05-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();
})();