Canvas 平台录播播放器翻天覆地彻头彻尾焕然一新加强插件

优化上海交通大学 Canvas 平台录播观看功能

目前為 2021-11-01 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Canvas 平台录播播放器翻天覆地彻头彻尾焕然一新加强插件
// @namespace    http://tampermonkey.net/
// @version      1.5.1
// @description  优化上海交通大学 Canvas 平台录播观看功能
// @author       danyang685
// @match        https://oc.sjtu.edu.cn/*
// @match        https://courses.sjtu.edu.cn/*
// @match        https://vshare.sjtu.edu.cn/play/*
// @icon         data:image/png;base64,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
// @grant        GM_info
// @grant        GM_addStyle
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

// 功能清单:
// [x] 将 vshare 网站的视频播放器替换为浏览器内置播放器
// [x] 到达 Canvas 登录(不可用)页时,自动跳转到 jAccount 登录(不可用)页
// [x] 在 Canvas 课程页面访问【课程视频】时,默认进入【点播】功能
// [x] 禁用了打开后自动开始播放视频的特性
// [x] 倍速列表选项优化(0.5-16倍)
// [x] 将全局右键屏蔽改为仅应用至视频区域
// [x] 状态栏时间显示帧率优化(由1秒刷新加速为0.05秒刷新)
// [x] 加快状态栏功能弹窗隐藏速度
// [x] 允许鼠标点击音量条任意位置设定音量并解除静音
// [x] 可以使用鼠标滚轮调节音量了,调节时会在画面左上角显示当前音量
// [x] 隐藏了小音量控制按钮,并在音量菜单增加了两路子音量控制
// [x] 状态栏显示倍速
// [x] 在调整倍速后自动关闭菜单
// [x] 空格键或单击画面暂停/播放
// [x] 双击画面全屏
// [x] 左右方向键进行时移(3秒)
// [x] Ctrl+左右方向键进行快速时移(15秒)
// [x] Ctrl+Shift+左右方向键进行超快速时移(59秒)
// [x] 使用键盘快捷键【Enter】切换全屏
// [x] 使用键盘快捷键ZXC进行变速控制
// [x] 使用键盘快捷键ASD进行默认播放速度调整
// [x] 可以在小画面上使用鼠标滚轮缩放画面
// [x] 修正视频变形问题(但导致了空边)
// [x] 将黑边修改为空白边
// [x] 鼠标位于进度条上或拖动进度条时浮窗显示时刻
// [x] 修复了暂停状态下改变进度条导致意外继续播放且进度条不再刷新的问题
// [x] 为小窗视频略微增加透明度
// [x] 修改默认音量设定为不静音,并移除静音说明
// [x] 在右上角增加【在新标签页播放】按钮
// [x] 清空了视频下方的说明文字
// [x] 使右侧视频列表的内容更紧凑,并增加了教室显示,并去除了时间显示的00秒部分
// [x] 列表好长好长好长的时候,自动将滚动条定位到当前视频条目
// [x] 在视频列表顶部文字中显示视频总数
// [x] 使用配色糟糕的标签突出显示未观看过的视频
// [x] 增加了画中画模式,并允许从设置中直接选择所需画面
// [x] 仅有一路视频时,禁用了画面布局按钮
// [x] 增加了双路视频手动同步功能,能够比较方便地进行画面同步
// [x] 支持注册(不可用) MediaSession 从而通过系统控制视频的播放与暂停
// [x] 阻止向服务器回报观看日志
// [x] 去除了画面中不必要的滚动条
// [x] 移除了缺乏使用场景的【停止播放】按钮
// [x] 自动更新cookie,防止页面会话失效
// [x] 当视频链接过期时,自动刷新链接(同时返回原进度)
// [x] 从canvas内直接打开【视频点播】时,自动切换到上次观看的视频
// [x] 打开视频后,自动跳转到上次观看的进度
// [x] 打开视频后,自动载入上次的默认播放速度
// [x] 为每个视频分别记忆时间同步参数,下次观看无需再设置
// [x] 重新打开时,记忆上次的小画面尺寸
// [x] 不同canvas用户之间,个人观看偏好不互通
// [x] 通过增加边框使右侧视频列表更有质感
// [x] 将画面背景色由廉价的灰色改为圣洁白
// [x] 将播放控制栏底色由廉价的灰色改为至尊黑
// [x] 本插件顺利生效时,顶部标签卡颜色为金色

(function () {
    'use strict';
    let script_version = GM_info.script.version; // 本脚本的版本号
    let window = unsafeWindow; // 脚本中使用GM函数后,必须使用 unsafeWindow 才可覆盖原有 window 事件回调函数

    // 登录(不可用)页面,要求选 jAccount 或校外用户登录(不可用)
    let is_canvas_login = window.location.href == "https://oc.sjtu.edu.cn/login/canvas";
    // 录播页面,包括 Canvas 内置的和 courses.sjtu 网站上的
    let is_vod_page = window.location.href.startsWith("https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage") || window.location.href.startsWith("https://courses.sjtu.edu.cn/app/vodvideo/vodVideoPlay.d2j");
    // 直播页面,Canvas 内置的
    let is_canvas_live_page = window.location.href == "https://courses.sjtu.edu.cn/lti/app/lti/liveVideo/index.d2j";
    // 直录播 LTI 插件页面
    let is_canvas_vodnlive_lti_page = new RegExp("https://oc\.sjtu\.edu\.cn/courses/\\d*/external_tools/162").test(window.location.href);
    // vshare 视频页面
    let is_vshare_page = window.location.href.startsWith("https://vshare.sjtu.edu.cn/play/")
    // 处于 iframe 内
    let is_iframe = (self != top);

    // 检查是否为安卓设备
    function isAndroid() {
        return navigator.userAgent.includes("Android");
    }

    // 允许进一步缩放
    if (isAndroid()) {
        // 怎么会一点也不起作用呢?一定是哪里出了问题,不应当不应当!
        // document.getElementById("viewport").setAttribute("content", "height=520, initial-scale=0, minimum-scale=0.25, maximum-scale=1.0, user-scalable=yes");
    }

    // 到达 Canvas 登录(不可用)页时,自动跳转到 jAccount 登录(不可用)页
    if (is_canvas_login) {
        window.location.replace("https://oc.sjtu.edu.cn/login/openid_connect");
    }

    // 在 Canvas 课程页面访问【课程视频】时,默认进入【点播】功能
    if (is_canvas_live_page) {
        if (document.referrer == "https://oc.sjtu.edu.cn/") {
            if (window.innerHeight == 0) { // 我是用于更新会话的工具iframe
                top.postMessage("done!", "https://oc.sjtu.edu.cn");
                return;
            }
            window.location.replace("https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage");
        }
    }

    // LTI插件页面也有点UI问题可以修改
    if (is_canvas_vodnlive_lti_page) {
        $(document).ready(function () {
            // 去除了画面中不必要的滚动条
            /*
            let set_height_interval = setInterval(function () {
                $(".ic-Layout-contentMain>.tool_content_wrapper")
                    .css("height", "100%");
            }, 100);
            */

            $(".screenreader-only").height(0);

            // 去除了左侧的空白区域
            $(".ic-Layout-contentMain")
                .css("height", "100%")
                .css("padding", "0");

            $("#tool_content")
                .css("min-height", "600px");

            // 自动更新cookie,防止页面会话失效
            function refresh_session() {
                $("body").append('<iframe src="' + window.location.href + '" style="display:none; height:0;" id="session_updater_iframe">');

            }

            let pending_request_refresh = false;

            // 将相关重要跨域传递给子页面
            $(window).on("message", function (event) {
                let origin = event.origin || event.originalEvent.origin;
                let data = event.data || event.originalEvent.data;

                if (origin == "https://courses.sjtu.edu.cn") {

                    switch (data) {
                        case "online!": {
                            document.getElementById("tool_content").contentWindow.postMessage(
                                JSON.stringify({
                                    "message_type": "config_tranfer",
                                    "course_canvasid": window.location.href.match(new RegExp("courses/(\\d*)/"))[1],
                                    "course_name": $("#context_title").attr("value"),
                                    "course_fullname": $("#context_label").attr("value"),
                                    "user_name": $("#lis_person_name_full").attr("value"),
                                    "user_id": $("#custom_canvas_user_id").attr("value")
                                }), "https://courses.sjtu.edu.cn");
                            break;
                        }
                        case "help!": {
                            refresh_session();
                            break;
                        }
                        case "helpr!": {
                            refresh_session();
                            pending_request_refresh = true;
                            break;
                        }
                        case "done!": {
                            $("#session_updater_iframe").remove();
                            if (pending_request_refresh) { // 需要发送刷新指令
                                pending_request_refresh = false;
                                document.getElementById("tool_content").contentWindow.postMessage(
                                    JSON.stringify({
                                        "message_type": "request_refresh"
                                    }), "https://courses.sjtu.edu.cn");
                            }
                            break;
                        }
                    }
                }
            })
        })
    }

    // 让 Canvas 平台录播播放器翻天覆地彻头彻尾焕然一新
    if (is_vod_page) {
        console.log("Canvas录播加强!");

        $(document).ready(function () {

            function setStorage(k, v) {
                localStorage.setItem("canvasnb_plugin_" + k, v);
            }

            function getStorage(k) {
                return localStorage.getItem("canvasnb_plugin_" + k);
            }

            // 首先获取本页面的课程ID
            let course_id = undefined; {
                let ma = document.getElementsByTagName('html')[0].outerHTML.match(new RegExp("canvasCourseId = '([0-9A-Z]*)';"));
                if (ma) {
                    course_id = ma[1];
                }
            }
            console.log("课程ID:" + course_id);
            if (!course_id) {
                window.location.reload();
            }

            let userid = getStorage("course_" + course_id + "_last_userid");

            if (is_iframe && document.referrer == 'https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage') {
                top.window.postMessage("online!", "https://oc.sjtu.edu.cn");
            }

            $(window).on("message", function (event) {
                let origin = event.origin || event.originalEvent.origin;
                let data = event.data || event.originalEvent.data;

                if (is_iframe && origin == "https://oc.sjtu.edu.cn") { // oc.sjtu仅会向iframe内的course.sjtu发送消息
                    let message = JSON.parse(data);

                    if (message["message_type"] == "config_tranfer") {
                        setStorage("course_" + course_id + "_realname", message["course_name"]);
                        setStorage("course_" + course_id + "_canvasid", message["course_canvasid"]);
                        setStorage("course_" + course_id + "_fullname", message["course_fullname"]);
                        setStorage("course_" + course_id + "_last_userid", message["user_id"]);
                        userid = message["user_id"];
                    } else if (message["message_type"] == "request_refresh") {
                        location.reload(); // 刷新页面
                    }
                }
            });

            function refresh_session(andRefresh = false) {
                if (andRefresh) {
                    top.postMessage("helpr!", "https://oc.sjtu.edu.cn");
                } else {
                    top.postMessage("help!", "https://oc.sjtu.edu.cn");
                }
            }
            if (is_iframe) {
                $(document).on('visibilitychange', function (event) {
                    if (!document.hidden) {
                        refresh_session();
                    }
                })
                setInterval(refresh_session, 20 * 60 * 1000); // 20分钟更新一次,防止页面失效
            }

            if (!window.location.href.includes("?id=")) // 从canvas内直接打开【视频点播】时,自动切换到上次观看的视频
            {
                let last_play = getStorage(userid + "_course_" + course_id + "_lastplay_video_id");
                if (last_play != null) {
                    window.location.href = window.location.href + "?id=" + last_play;
                }
            }

            // 覆盖已有设定,有一说一,光屏蔽F12有用吗,Chrome还可以 Ctrl+Shift+I 呢。
            window.onkeydown = window.onkeyup = window.onkeypress = window.oncontextmenu = undefined;

            function work() {
                let progressBar_operating_flag = false, // 手动拖动进度条的过程中,不应覆写进度条状态
                    is_single_video = undefined,
                    current_time_speed = 1.0,
                    default_time_speed = 1.0,
                    current_zoom_ratio = parseInt(getStorage(userid + "_zoom_ratio")) || 45; // 重新打开时,记忆上次的小画面尺寸

                // 再获取本视频的视频ID(等右边栏加载出来再说)
                let video_id = $(".lti-list .list-item--active").attr("id");
                console.log("视频ID:" + video_id);

                // 使右侧视频列表的内容更紧凑了,并增加了教室显示
                let this_teacher;
                // 在视频列表顶部文字中显示视频总数
                $(".lti-list>.list-title").append($('<span style="font-size: 50%;">(' + $(".lti-list .item-text").length + '条视频)</span>'));
                $(".lti-list .item-text").each(function (idx, elem) {
                    elem = $(elem);
                    elem.children("p:first").attr("title", ""); // 清空课程名title
                    elem.children(".classroom-name").css("display", "");
                    let course_name = elem.children("p:first").html();
                    let teacher_name = elem.children(".item-contour").html();
                    teacher_name = teacher_name.substr(3, teacher_name.length - 1).trim();
                    elem.children("p:first").html(course_name + "(" + teacher_name + ")"); // 课程名加教师名
                    elem.children(".item-contour").remove(); // 删除教师
                    let course_time = elem.children("p:last-child").html();
                    elem.children("p:last-child").html(course_time.trim().substr(0, course_time.length - 3));
                    if (elem.attr("class").includes("list-item--active")) {
                        this_teacher = teacher_name;
                    }

                    // 使用配色糟糕的标签突出显示未观看过的视频
                    let elem_video_id = elem.parent().attr("id");
                    if (getStorage(userid + "_video_" + elem_video_id + "_position") == null && video_id != elem_video_id) {
                        let terrible_label = '<div style="position: relative;top: -41px;left: 139px;width: 0;height:0;"><span style="text-align: center; position: absolute; background-color: #00ac18; border-radius: 10px; height: 30px; line-height: 30px; width: 30px; font-size: 20px; font-weight: bold; color: red;">新</span></div>';
                        elem.append($(terrible_label));
                    }
                });

                // 将右侧视频列表改为升序了
                // $(".list-main").html($(".list-main>.list-item").get());

                // 列表好长好长好长的时候,自动将滚动条定位到当前视频条目
                $(".list-main")[0].scrollTop = $(".lti-list .list-item--active")[0].offsetTop - 250;
                //$(".lti-list .list-item--active")[0].scrollIntoView({block: "center"}); // 这个不太行,把整个页面都给滚没了

                // 从canvas内直接打开【视频点播】时,自动切换到上次观看的视频
                setStorage(userid + "_course_" + course_id + "_lastplay_video_id", $(".lti-list .list-item--active").attr("id"));

                // 是否正在播放/暂停
                function isPlaying() {
                    return $(".tool-btn__play").css("display") == "none";
                }

                // 设置播放/暂停
                function setPlay(status, quiet = false) {
                    if (status) {
                        kmplayer.play("play");
                        navigator.mediaSession.playbackState = "playing";
                        if (!quiet) {
                            putText("状态:播放");
                        }
                    } else {
                        kmplayer.play("pause");
                        navigator.mediaSession.playbackState = "paused";
                        if (!quiet) {
                            putText("状态:暂停");
                        }
                    }
                }

                // 切换播放/暂停
                function togglePlay() {
                    setPlay(!isPlaying());
                }

                // 设定播放速度
                function setSpeed(speed) {
                    for (let i = 0; i < kmplayer.ids.length; i++) {
                        kmplayer.allInstance['type' + (i + 1)].playbackRate(speed);
                    }
                    $("#timesContorl>span").html(speed);
                    $("#timesContorl>ul>li").each(function (idx, elem) {
                        if (elem.id == speed) {
                            $(elem).attr("class", "times-active");
                        } else {
                            $(elem).attr("class", "");
                        }
                    });
                }

                // 读取播放速度
                function getSpeed() {
                    return kmplayer.allInstance.type1.playbackRate();
                }

                // 设定播放位置
                function setTime(time) {
                    kmplayer.timeUpdateFlag = false; // 它里面就这么写的
                    kmplayer.setKMediaRate(time);
                }

                // 读取播放位置
                function getTime() {
                    return kmplayer.allInstance.type1.currentTime();
                }

                // 重写状态栏播放状态
                function updateTimeText() {
                    if (progressBar_operating_flag) {
                        return;
                    }
                    kmplayer.setViewSenTime(getTime()); // 时间显示同步
                    kmplayer.addSpeedRate(getTime()); // 进度条同步
                }

                // 设定音量
                let scene_audio_ratio = 1.00;
                let computer_audio_ratio = 1.00;

                function setVolume(volume_ratio, idx) {
                    volume_ratio = volume_ratio > 1 ? 1 : volume_ratio;
                    volume_ratio = volume_ratio < 0 ? 0 : volume_ratio;
                    volume_ratio *= 100;
                    switch (idx) {
                        case 0:
                            kmplayer.volume = volume_ratio;
                            kmplayer.setVolume(volume_ratio);
                            rewriteVolume();
                            $(".voice0-rate").height(volume_ratio);
                            kmplayer.voice("muted");
                            break;
                        case 1:
                            $(".voice" + idx + "-rate").height(volume_ratio);
                            scene_audio_ratio = volume_ratio / 100;
                            break;
                        case 2:
                            $(".voice" + idx + "-rate").height(volume_ratio);
                            computer_audio_ratio = volume_ratio / 100;
                            break;
                        default:
                            break;
                    }
                }

                // 为两路声音设置均衡
                function rewriteVolume() {
                    if (kmplayer.allInstance.type1.volume()) {
                        kmplayer.allInstance.type1.volume(kmplayer.volume * scene_audio_ratio);
                    }
                    if (kmplayer.allInstance.type2.volume()) {
                        kmplayer.allInstance.type2.volume(kmplayer.volume * computer_audio_ratio);
                    }
                }

                // 为两路画面设置同步
                function syncTime() {
                    let delta = kmplayer.allInstance.type2.currentTime() - kmplayer.allInstance.type1.currentTime() - getTimeDelta();
                    if (Math.abs(delta) > 0.2) {
                        kmplayer.allInstance.type2.currentTime(getTime() + getTimeDelta());
                    }
                }

                // 设定小画面的尺寸
                function setSmallVideoSize(size) {
                    setStorage(userid + "_zoom_ratio", size); // 重新打开时,记忆上次的小画面尺寸
                    let num2 = parseInt(size),
                        num1 = 100 - num2;
                    $("style").each(function (idx, elem) {
                        if (elem.innerHTML.includes(".style-type-2-1 .cont-item-2 {top: ")) {
                            $(elem).remove();
                        }
                    })
                    GM_addStyle(".style-type-2-1 .cont-item-2 {top: " + num1 + "%;left: " + num1 + "%; width: " + num2 + "%; height: " + num2 + "%;}");
                }
                setSmallVideoSize(current_zoom_ratio); // 首先设置一遍

                // 打开视频后,自动跳转到上次观看的进度
                let last_playback = getStorage(userid + "_video_" + video_id + "_position")
                if (last_playback != null) {
                    last_playback = parseFloat(last_playback);
                    if (last_playback > 10) { // 仅调整观看超过10秒的视频
                        let restore_time_interval = setInterval(function () {
                            kmplayer.allInstance.type1.currentTime(parseFloat(last_playback));
                            if (kmplayer.allInstance.type1.currentTime() >= last_playback - 2) {
                                clearInterval(restore_time_interval);
                                putText("已恢复到上次的播放进度:" + ("0" + parseInt(last_playback / 60)).slice(-2) + ":" + ("0" + parseInt(last_playback % 60)).slice(-2));
                            }
                        }, 100);
                    }
                }

                let time_sync_delta = 0;
                let last_time_sync = getStorage("video_" + video_id + "_timedelta");
                if (last_time_sync != null) {
                    time_sync_delta = parseFloat(last_time_sync);
                }

                function setTimeDelta(time_delta) {
                    time_sync_delta = time_delta;
                    setStorage("video_" + video_id + "_timedelta", time_delta);
                }

                function getTimeDelta() {
                    return time_sync_delta;
                }

                // 读取音量
                function getVolume() {
                    return kmplayer.volume / 100;
                }

                function getDuration() {
                    return kmplayer.durationSec;
                }

                // 输出左上角渐隐提示文本
                function putText(text) {
                    $("#custom-status-text>span").html(text);
                    $("#custom-status-text").finish().show().delay(1000).fadeOut(1000); // 完成前stoptruetrue的话,会导致delay不生效,为什么呢?
                }

                // 打开后默认不自动播放视频
                let auto_pause_interval = setInterval(function () {
                    if (getTime() != 0) {
                        clearInterval(auto_pause_interval);
                        setPlay(false, true);

                        // 打开视频后,自动载入上次的默认播放速度
                        if (getStorage(userid + "_default_speed_val") !== null) {
                            default_time_speed = parseFloat(getStorage(userid + "_default_speed_val")); // 传入字符串时,无效
                            console.log("load default speed: " + default_time_speed);
                            current_time_speed = default_time_speed;
                            // 应用记忆中的播放速度
                            setSpeed(current_time_speed);
                        }
                    }
                }, 50);

                // 特殊判断只有一个视频流的点播视频
                is_single_video = $(".cont-item-2").length == 0;

                // 双击画面全屏
                $("video").on('dblclick', function () {
                    kmplayer.scrren(); // 这都能拼错?
                    putText("切换全屏");
                    event.preventDefault(); // 阻断默认全屏行为
                    return false;
                });

                // 空格键或单击画面暂停/播放
                $("video").on('click', function () {
                    togglePlay();
                });

                // 隐藏画面上的音量控制
                $(".voice-icon").hide();

                // 修正视频变形问题(但导致了空边)
                $("video").css("object-fit", "contain");

                // 将黑边修改为空白边
                $(".kmd-app-container").css("background-color", "#0000");
                $(".kmd-app").css("background-color", "#0000");
                $(".kmd-container").css("background-color", "#0000");
                $(".kmd-player").css("background-color", "#0000");
                $("#rtcContent").css("background-color", "#0000");

                $("#rtcMain").focus(); // 说不定有点用?

                // 清空了视频下方的说明文字
                $(".course-details").empty();

                // 支持注册(不可用) MediaSession 从而通过系统控制视频的播放与暂停
                if ('mediaSession' in navigator) {
                    let elem = $(".list-main .list-item--active>.item-text")[0];
                    let media_title = $(elem).children("p:first").html();
                    let media_artist = this_teacher;
                    navigator.mediaSession.metadata = new MediaMetadata({
                        title: media_title,
                        artist: media_artist,
                    });
                    navigator.mediaSession.setActionHandler('play', function () {
                        setPlay(true)
                    });
                    navigator.mediaSession.setActionHandler('pause', function () {
                        setPlay(false)
                    });
                    navigator.mediaSession.setActionHandler('stop', function () {
                        kmplayer.stop("stop")
                    });
                    navigator.mediaSession.playbackState = "paused";

                    // navigator.mediaSession.setActionHandler('seekbackward', function() { /* Code excerpted. */ });
                    // navigator.mediaSession.setActionHandler('seekforward', function() { /* Code excerpted. */ });
                    // navigator.mediaSession.setActionHandler('seekto', function() { /* Code excerpted. */ });
                    // navigator.mediaSession.setActionHandler('previoustrack', function() { /* Code excerpted. */ });
                    // navigator.mediaSession.setActionHandler('nexttrack', function() { /* Code excerpted. */ });
                    // navigator.mediaSession.setActionHandler('skipad', function() { /* Code excerpted. */ });
                }

                // 加快状态栏功能弹窗隐藏速度
                function new_hoverleave_callback(cld, prt) {
                    let timer = null;
                    prt.on('mouseover', function () {
                        clearTimeout(timer);
                        cld.show();
                    })
                    prt.on("mouseout", function () {
                        timer = setTimeout(function () {
                            cld.hide();
                        }, 100);
                    });
                }
                new_hoverleave_callback($(".voice-volume"), $("#voiceContorl"));
                new_hoverleave_callback($(".split-select"), $("#splitContorl"));
                new_hoverleave_callback($("#timesContorl>ul"), $("#timesContorl"));

                // 修改默认音量设定为不静音,并移除静音说明
                kmplayer.voice("voice!");
                $(".mute-tip").hide();

                // 当视频链接过期时,自动刷新链接
                function refreshVideoLink() { // 本函数效果不理想,更改视频源后,画面将复位
                    $.ajax({
                        type: "POST",
                        url: "/lti/vodVideo/getVodVideoInfos",
                        async: true,
                        traditional: true,
                        dataType: "json",
                        data: {
                            playTypeHls: true,
                            id: video_id,
                            isAudit: true
                        },
                        success: function (data) {
                            let video_link_array = data.body.videoPlayResponseVoList;
                            let current_time = getTime();
                            $(".kmd-wrapper video#kmd-video-player").each(function (idx, elem) {
                                elem.src = video_link_array[idx].rtmpUrlHdv;
                            })
                            setTime(current_time);
                            console.log(video_link_array);
                        }
                    });
                }
                //setInterval(()=>refresh_session(true),8000);
                //setInterval(()=>refreshVideoLink(),8000);
                $(".kmd-wrapper video#kmd-video-player").each(function (idx, elem) {
                    elem.onerror = function () {
                        console.log("video error: " + elem.error.code + "; details: " + elem.error.message);
                        if (is_iframe) {
                            refresh_session(true); // 然后在onmessage里刷新页面
                        } else { // 独立窗口中无法自动更新会话,切回大窗口
                            window.location.href("https://oc.sjtu.edu.cn/courses/" + getStorage("course_" + course_id + "_canvasid") + "/external_tools/162");
                        }
                    }
                })

                function periodic_job() {
                    // 状态栏时间显示帧率优化(由1秒刷新加速为0.05秒刷新)
                    updateTimeText();
                    // 为小窗视频略微增加透明度
                    $(".cont-item-2 #kmd-video-player").css("filter", "brightness(1) contrast(1) saturate(1) hue-rotate(0deg) opacity(0.85)");
                    $(".cont-item-1 #kmd-video-player").css("filter", "brightness(1) contrast(1) saturate(1) hue-rotate(0deg)");

                    // 有两路视频时
                    if (!is_single_video) {
                        rewriteVolume(); // 为两路声音设置均衡
                        syncTime(); // 开启画面同步控制功能
                        if ($(".kmd-wrapper #kmd-video-player")[0].paused != $(".kmd-wrapper #kmd-video-player")[1].paused) {
                            setPlay(false, true); // 修复禁用启动后自动播放时的冲突导致的可能的暂停不彻底
                        }
                    }

                    // 支持注册(不可用) MediaSession 从而通过系统控制视频的播放与暂停
                    if ('mediaSession' in navigator) {
                        navigator.mediaSession.setPositionState({
                            duration: getDuration(),
                            playbackRate: parseFloat($("#timesContorl>span").html()) || 1,
                            position: getTime()
                        });
                    }

                    // 记录当前进度
                    if (isPlaying()) {
                        if (getStorage(userid + "_video_" + video_id + "_position") && getTime() < 3) { // 本次根本没播放
                        } else {
                            setStorage(userid + "_video_" + video_id + "_position", getTime());
                        }
                    }
                }

                setInterval(periodic_job, 50); // 50ms 的定时任务

                // 倍速列表选项优化(0.5-16倍)
                let speed_choice = [0.5, 0.75, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 3, 3.5, 4, 5, 6, 8, 16]; // 16是该播放器支持的最高倍速
                speed_choice.sort(function (a, b) {
                    return a - b;
                });
                let speed_choice_text = speed_choice.map(function (num) {
                    return '<li id="' + num + '">' + num + '倍</li>';
                }).join("");
                $("#timesContorl>ul").html(speed_choice_text);
                $("#timesContorl>ul>#1").attr("class", "times-active"); // 默认1倍高亮

                // 在调整倍速后自动关闭菜单
                $("#timesContorl").bind("click", function (event) {
                    if (event.target.tagName == "LI") {
                        // 状态栏显示倍速
                        $("#timesContorl>span").html(event.target.id);
                        $("#timesContorl>ul").css("display", "none");
                        current_time_speed = parseFloat(event.target.id);
                        putText("设定倍速:" + current_time_speed);
                    }
                    return true;
                });

                // 移除了缺乏使用场景的【停止播放】按钮
                $(".tool-btn__stop").remove();

                // 增加了画中画模式,并允许从设置中直接选择所需画面
                let split_choice_id = ["split_scene_only", "split_computer_only", "split_big_small", "split_pip"];
                let split_choice_desc = ["仅现场画面", "仅电脑画面", "一大一小", "进入画中画"];
                let split_choice_class = ["style-type-1-1", "style-type-1-1", "style-type-2-1", "style-type-2-1 style-type-2-1-pip"];
                let split_choice_text = split_choice_id.map(function (id, idx) {
                    return '<li id="' + id + '" class="' + split_choice_class[idx] + '">' + split_choice_desc[idx] + '</li>';
                }).join("");
                $("#splitContorl>ul").html(split_choice_text);
                $("#splitContorl>ul>#split_big_small").css("background", "white").css("color", "#0a0a0a"); // 默认一大一小
                // 仅有一路视频时,禁用各类画面布局功能
                if (is_single_video) {
                    $("#splitContorl").remove();
                }
                // 移动端似乎不支持画中画
                if (isAndroid()) {
                    $("#splitContorl>ul>#split_pip").remove();
                }
                // 有两路视频时,开启子音量控制功能
                $(".voice-volume .voice-max").attr("class", "voice-max voice0-max");
                $(".voice-volume .voice-rate").attr("class", "voice-rate voice0-rate");
                if (!is_single_video) {
                    $(".voice-volume").css("width", "80px").css("text-align", "center");
                    $(".voice-volume").append($('<div class="voice-max voice1-max" style="width: 8px;"><div class="voice-rate voice1-rate voice-rate-child" style="height: 100%;"><div class="tool-rate-tip"></div></div></div>'));
                    $(".voice-volume").append($('<div class="voice-max voice2-max" style="width: 8px;"><div class="voice-rate voice2-rate voice-rate-child" style="height: 100%;"><div class="tool-rate-tip"></div></div></div>'));
                    $(".voice-volume>div").css("display", "inline-block").css("margin", "0 9px");
                    $(".voice-volume").css("transform", "translateX(-35px)");
                    $(".voice-volume").append($('<span class="voice-desc voice0-desc">总控</span>'));
                    $(".voice-volume").append($('<span class="voice-desc voice1-desc">现场</span>'));
                    $(".voice-volume").append($('<span class="voice-desc voice2-desc">电脑</span>'));
                    $(".voice-volume>span").css("position", "absolute")
                        .css("top", "50%")
                        .css("font-size", "12px")
                        .css("width", "50px");
                    $(".voice-volume>.voice0-desc").css("transform", "translateX(-87px) translateY(50px)");
                    $(".voice-volume>.voice1-desc").css("transform", "translateX(-62px) translateY(50px)");
                    $(".voice-volume>.voice2-desc").css("transform", "translateX(-37px) translateY(50px)");
                }

                // 有两路视频时,能够通过设定参考点的方式进行画面同步
                if (!is_single_video) {
                    $('<div id="syncControl" class="tool-bar-item tool-btn__times" style="position: relative; z-index: 20;"><span>同步</span><div><p>设定参考点以进行同步</p><input type="button" value="现场画面参考"><input type="button" value="电脑画面参考"><p>还原</p><input type="button" value="还原默认"> \</div></div>')
                        .insertAfter("#timesContorl");
                    $("#syncControl")
                        .css("position", "relative")
                        .css("z-index", "20");
                    $("#syncControl>div")
                        .css("position", "absolute")
                        .css("display", "none")
                        .css("bottom", "25px")
                        .css("left", "-58px")
                        .css("height", "100px")
                        .css("width", "150px")
                        .css("border-radius", "3px")
                        .css("background-color", "rgba(28, 32, 44, .9)");
                    let sync_ref_scene = -1;
                    let sync_ref_computer = -1;

                    function timeSec2Text(time) {
                        let time_s = parseInt(time);
                        let time_ms = parseInt((time - parseInt(time)) * 1000);
                        let time_min = parseInt(time_s / 60);
                        let time_sec = parseInt(time_s % 60);
                        return ("0" + parseInt(time_min)).slice(-2) + ":" + ("0" + parseInt(time_sec)).slice(-2) + "." + ("00" + parseInt(time_ms)).slice(-3);
                    }

                    function clear_pending_sync_control_button() {
                        $("#syncControl :button").each(function (idx, elem) {
                            if (elem.value.includes("撤销")) {
                                elem.value = elem.value.substr(2, elem.value.length - 1);
                            }
                        });
                    }
                    $("#syncControl :button").on("click", function (event) {
                        switch (event.target.value) {
                            case "现场画面参考": {
                                sync_ref_scene = kmplayer.allInstance.type1.currentTime();
                                if (sync_ref_computer == -1) {
                                    putText("ref_scene: " + timeSec2Text(sync_ref_scene) + ", waiting for ref_computer...");
                                    event.target.value = "撤销现场画面参考";
                                } else {
                                    setTimeDelta(sync_ref_computer - sync_ref_scene);
                                    putText("delta: " + time_sync_delta.toFixed(3) + "s");
                                    sync_ref_scene = -1;
                                    sync_ref_computer = -1;
                                    clear_pending_sync_control_button();
                                }

                                break;
                            }
                            case "电脑画面参考": {
                                sync_ref_computer = kmplayer.allInstance.type2.currentTime();
                                if (sync_ref_scene == -1) {
                                    putText("ref_computer: " + timeSec2Text(sync_ref_computer) + ", waiting for ref_scene...");
                                    event.target.value = "撤销电脑画面参考";
                                } else {
                                    setTimeDelta(sync_ref_computer - sync_ref_scene);
                                    putText("delta: " + time_sync_delta.toFixed(3) + "s");
                                    sync_ref_scene = -1;
                                    sync_ref_computer = -1;
                                    clear_pending_sync_control_button();
                                }
                                break;
                            }
                            case "撤销现场画面参考": {
                                sync_ref_scene = -1;
                                event.target.value = "现场画面参考";
                                break;
                            }
                            case "撤销电脑画面参考": {
                                sync_ref_computer = -1;
                                event.target.value = "电脑画面参考";
                                break;
                            }
                            case "还原默认": {
                                sync_ref_scene = -1;
                                sync_ref_computer = -1;
                                putText("resetted!");
                                clear_pending_sync_control_button();
                                setTimeDelta(0);
                                break;
                            }
                        }
                    });
                    new_hoverleave_callback($("#syncControl>div"), $("#syncControl"));
                }

                // 切换画中画设定
                function enable_PiP(flag) {
                    if (flag) {
                        let video2 = $(".cont-item-2 #kmd-video-player"); // 右下角视频
                        video2.on('enterpictureinpicture', function () {
                            $(".cont-item-2").hide(); // 进入画中画,隐藏原小窗
                            $("#split_pip").html("退出画中画");
                        });
                        video2.on('leavepictureinpicture', function () {
                            $(".cont-item-1").css("display", ""); // 退出画中画,恢复原小窗
                            $(".cont-item-2").css("display", ""); // 退出画中画,恢复原小窗
                            $("#split_pip").html("进入画中画");
                            $("#split_big_small").click();
                        });
                        video2[0].requestPictureInPicture();
                    } else {
                        document.exitPictureInPicture();
                    }
                }

                let split_replaying_flag = false;
                $("#splitContorl").bind("click", function (event) {
                    if (event.target.tagName == "LI") {
                        $(event.target).css("background", "white").css("color", "#0a0a0a");
                        $(event.target).siblings().each(function (idx, elem) {
                            $(elem).css("background", "").css("color", "");
                        });
                        let changed_flag = false;
                        if ($("#split_pip").html().includes("退出")) {
                            enable_PiP(false);
                            $("#split_pip").html("进入画中画");
                            changed_flag = true;
                        }
                        switch ($(event.target).attr("id")) {
                            case "split_computer_only": // 仅电脑画面
                                $("#player-00002").parent(".rtc-cont-item").attr("class", "rtc-cont-item cont-item-1");
                                $("#player-00001").parent(".rtc-cont-item").attr("class", "rtc-cont-item cont-item-2");
                                if (!split_replaying_flag) {
                                    split_replaying_flag = true;
                                    setTimeout(function () {
                                        $(event.target).click();
                                        split_replaying_flag = false;
                                    }, 1);
                                }
                                break;
                            case "split_scene_only": // 仅现场画面
                                $("#player-00001").parent(".rtc-cont-item").attr("class", "rtc-cont-item cont-item-1");
                                $("#player-00002").parent(".rtc-cont-item").attr("class", "rtc-cont-item cont-item-2");
                                if (!split_replaying_flag) {
                                    split_replaying_flag = true;
                                    setTimeout(function () {
                                        $(event.target).click();
                                        split_replaying_flag = false;
                                    }, 1);
                                }
                                break;
                            case "split_pip":
                                if ($(event.target).html().includes("进入") && !changed_flag) {
                                    enable_PiP(true);
                                    // $(event.target).html("退出画中画");
                                }
                                break;
                            default:
                                break;
                        }
                    }
                });

                // 鼠标位于进度条上或拖动进度条时浮窗显示时刻
                $("#rtcContent").append('<div id="custom-progress-hover"><svg viewBox="-3 -3 37.6 23"> <polygon points="17.3,20 0,0 34.6,0" style="fill:#0092E9AA; stroke:black; stroke-width:3;"/></svg><span>12:00</span></div>');
                $("#custom-progress-hover") // 进度条,百万大制作
                    .css("position", "absolute")
                    .css("text-align", "center")
                    .css("display", "none")
                    .css("background-color", "#0092E9AA")
                    .css("border", "2px solid black")
                    .css("border-radius", "10px")
                    .css("box-shadow", "#AAA 0px 0px 10px")
                    .css("margin", "8px 8px")
                    .css("z-index", "11")
                    .css("padding", "5px 5px;")
                    .css("width", "60px")
                    .css("height", "25px");
                $("#custom-progress-hover span")
                    .css("user-select", "none")
                    .css("position", "absolute")
                    .css("top", "50%")
                    .css("transform", "translateX(-50%) translateY(-50%)")
                    .css("font-weight", "bold")
                    .css("font-size", "14px");
                $("#custom-progress-hover svg")
                    .css("position", "absolute")
                    .css("top", "50%")
                    .css("width", "15px")
                    .css("transform", "translateX(-50%) translateY(140%)");

                $("#rtcTool .tool-bar").css("background-color", "black");
                $("#rtcContent").css("background-color", "black");

                // 浮窗显示
                $(".tool-progress").on("mousemove", function (event) {
                    let progress_ratio = (event.pageX - $(".tool-progress").offset().left) / $(".tool-progress").width();
                    progress_ratio = progress_ratio < 0 ? 0 : progress_ratio;
                    progress_ratio = progress_ratio > 1 ? 1 : progress_ratio;

                    $("#custom-progress-hover").css("display", "block");
                    $("#custom-progress-hover").css("top", $("#rtcTool").position().top - 48);
                    $("#custom-progress-hover").css("left", event.pageX - $("#rtcTool").offset().left - 40);

                    let progress_sec = parseInt(progress_ratio * getDuration());

                    $("#custom-progress-hover>span").html(parseInt(progress_sec / 60) + ":" + ("0" + parseInt(progress_sec % 60)).slice(-2));
                });

                $(".tool-progress").on("mouseleave", function (event) {
                    $("#custom-progress-hover").css("display", "none");
                });

                // 扩展进度条的可点击范围高度,便于操作
                GM_addStyle(".tool-progress {z-index: 12;}")
                GM_addStyle(".tool-progress > div {height:4px !important; position:absolute !important;}")
                $(".tool-progress").append('<div style="width: 100%; height: 25px !important; background-color: transparent; transform: translateY(-16px); cursor:auto; z-index: 11;" class="tool-progress"></div>');

                // 在画面左上角显示自动渐隐的文本,显示音量变化
                $("#rtcContent").append('<div id="custom-status-text"><span></span></div>');
                $("#custom-status-text")
                    .css("position", "absolute")
                    .css("text-align", "center")
                    .css("display", "none")
                    .css("background-color", "#FFF8")
                    .css("margin", "5px 5px")
                    .css("z-index", "11")
                    .css("padding", "2px 2px;")
                    .css("height", "20px")
                    .css("top", 0)
                    .css("left", event.pageX - $("#rtcMain").offset().left);
                $("#custom-status-text>span")
                    .css("font-size", "16px")
                    .css("color", "blue");

                // 修复了暂停状态下改变进度条导致意外继续播放且进度条不再刷新的问题
                function repair_pause_playing() {
                    if (!isPlaying()) {
                        setTimeout(function () {
                            if (!isPlaying()) {
                                setPlay(false, true); // 这种情况下不展示消息提醒
                                updateTimeText();
                                // console.log("repairing...");
                            }
                        }, 100);
                    }
                }
                $("body").on("mouseup", function (event) {
                    // console.log("mouseup",event.originalEvent.button);
                    if (event.originalEvent.button != 0) { // 仅响应鼠标左键
                        return;
                    }

                    let target_el_class = $(event.target).attr("class");
                    // 使用按钮进行播放和暂停也可以显示文字提示了
                    if (Object.prototype.toString.call(target_el_class) === "[object String]") {
                        if ($(event.target).attr("class").includes("tool-btn__play")) {
                            putText("状态:播放");
                        } else if ($(event.target).attr("class").includes("tool-btn__pause")) {
                            putText("状态:暂停");
                        }
                    }

                    progressBar_operating_flag = false;
                    repair_pause_playing();
                    //return true;
                });
                $("body").on("mousedown", function (event) {
                    // console.log("mousedown",event.originalEvent.button);
                    if (event.originalEvent.button != 0) { // 仅响应鼠标左键
                        return;
                    }

                    if (!$(event.target).attr("class")) {

                    } else if ($(event.target).attr("class").startsWith("tool-progress")) {
                        // 避免鼠标操作进度条时自动刷新进度条
                        progressBar_operating_flag = true;
                    } else if ($(event.target).attr("class").startsWith("voice-")) {
                        // 允许鼠标点击音量条任意位置设定音量并解除静音
                        let click_height = event.pageY;
                        let clicked_idx_match = $(event.target).attr("class").match(new RegExp("voice(\d)-"));
                        if (clicked_idx_match) {
                            let clicked_idx = parseInt(clicked_idx_match[1]);
                            let voicebar_height = $(".voice0-max").height();
                            let voicebar_base = $(".voice0-max").offset().top;
                            let volume_ratio = 1 - (click_height - voicebar_base) / voicebar_height;
                            setVolume(volume_ratio, clicked_idx);
                        }
                    }
                    //return true;
                });
                $("body").on("keyup", function (event) {
                    // console.log("keyup:",event.target);
                    switch (event.keyCode) {
                        case 37: // 方向键左
                        case 39: { // 方向键右
                            repair_pause_playing();
                            break;
                        }
                        default:
                            break;
                    }
                });

                $("body").on("keydown", function (event) {
                    // console.log("keydown:",event.target,event.keyCode);

                    // 左右方向键进行时移
                    // Ctrl+左右方向键进行快速时移
                    // Ctrl+Shift+左右方向键进行超快速时移
                    const SMALL_TIME_STEP = 3;
                    const MIDDLE_TIME_STEP = 15;
                    const BIG_TIME_STEP = 59;
                    let step = SMALL_TIME_STEP;
                    if (event.ctrlKey && event.shiftKey) {
                        step = BIG_TIME_STEP;
                    } else if (event.ctrlKey) {
                        step = MIDDLE_TIME_STEP;
                    }

                    let is_using_default_speed = (Math.abs(getSpeed() - default_time_speed) < 0.005);
                    switch (event.keyCode) {
                        case 32: { // 空格
                            // 空格键或单击画面暂停/播放
                            togglePlay();
                            break;
                        }
                        case 39: { // 方向键右
                            let target_time = getTime() + step;
                            kmplayer.timeUpdateFlag = false;
                            if (target_time > kmplayer.durationSec) {
                                target_time = kmplayer.durationSec;
                            }
                            setTime(target_time);
                            // console.log("add:", step, "to:", target_time);
                            break;
                        }
                        case 37: { // 方向键左
                            let target_time = getTime() - step;
                            kmplayer.timeUpdateFlag = false;
                            if (target_time < 0) {
                                target_time = 0;
                            }
                            setTime(target_time);
                            // console.log("minus:", step, "to:", target_time);
                            break;
                        }
                        case 67: { // 字母C
                            current_time_speed = Math.round(current_time_speed * 10 + 1) / 10;
                            if (current_time_speed > 16) {
                                current_time_speed = 16;
                            }
                            putText("增加倍速:" + current_time_speed.toFixed(1));
                            setSpeed(current_time_speed);
                            break;
                        }
                        case 88: { // 字母X
                            current_time_speed = Math.round(current_time_speed * 10 - 1) / 10;
                            if (current_time_speed < 0.1) {
                                current_time_speed = 0.1;
                            }
                            putText("减小倍速:" + current_time_speed.toFixed(1));
                            setSpeed(current_time_speed);
                            break;

                        }
                        case 90: { // 字母Z
                            if (is_using_default_speed) {
                                setSpeed(current_time_speed);
                                putText("恢复倍速:" + current_time_speed.toFixed(1));
                            } else {
                                setSpeed(default_time_speed);
                                putText("暂停倍速:" + default_time_speed.toFixed(1));
                            }
                            break;
                        }
                        case 65: { // 字母A
                            default_time_speed = 1.0;
                            putText("恢复默认默认倍速:1.0");
                            if (is_using_default_speed) {
                                setSpeed(default_time_speed);
                            }
                            setStorage(userid + "_default_speed_val", default_time_speed);
                            break;
                        }
                        case 83: { // 字母S
                            default_time_speed = Math.round(default_time_speed * 10 - 1) / 10; // 不然会有那么那么多零。。。
                            if (default_time_speed < 0.1) {
                                default_time_speed = 0.1;
                            }
                            putText("减小默认倍速:" + default_time_speed.toFixed(1));
                            if (is_using_default_speed) {
                                setSpeed(default_time_speed);
                            }
                            setStorage(userid + "_default_speed_val", default_time_speed);
                            break;
                        }
                        case 68: { // 字母D
                            default_time_speed = Math.round(default_time_speed * 10 + 1) / 10; // 不然会有那么那么多零。。。
                            if (default_time_speed > 16) {
                                default_time_speed = 16;
                            }
                            putText("增加默认倍速:" + default_time_speed.toFixed(1));
                            if (is_using_default_speed) {
                                setSpeed(default_time_speed);
                            }
                            setStorage(userid + "_default_speed_val", default_time_speed);
                            break;
                        }
                        case 13: { // 使用键盘快捷键【Enter】切换全屏
                            kmplayer.scrren(); // 这都能拼错?
                            putText("切换全屏");
                            break;
                        }
                        default:
                            return true;
                    }
                    event.preventDefault();
                    return false;
                });

                $("body").on("wheel", function (event) {
                    // console.log("wheel",event.originalEvent.deltaY);

                    // 仅在视频上使用滚轮生效
                    if (event.target.tagName != "VIDEO") {
                        return;
                    }
                    // 可以在小画面上使用鼠标滚轮缩放画面
                    if (event.target == $(".cont-item-2 #kmd-video-player")[0]) {
                        let max_ratio = 80,
                            min_ratio = 15;
                        current_zoom_ratio -= event.originalEvent.deltaY / 25;
                        current_zoom_ratio = current_zoom_ratio > max_ratio ? max_ratio : current_zoom_ratio;
                        current_zoom_ratio = current_zoom_ratio < min_ratio ? min_ratio : current_zoom_ratio;
                        setSmallVideoSize(current_zoom_ratio);
                    } else {
                        let volDelta = event.originalEvent.deltaY / 2500;
                        setVolume(getVolume() - volDelta, 0);
                        // 使用滚轮调节音量时,会在画面左上角显示自动渐隐的文本,显示音量变化
                        putText("Volume: " + parseInt(getVolume() * 100));
                    }
                });


                // 在右上角增加【在新标签页播放】按钮
                if (is_iframe) { // 【在新标签页播放】时不再显示【在新标签页播放】按钮
                    $(".lti-page-tab").append($('<button class="tab-help" onclick="window.open(window.location.href)" id="btn_play_in_new_tab">在新标签页播放</button>'));
                }
                $("#btn_play_in_new_tab").on("contextmenu", function (event) { // 右击能在新标签页打开有意思的东西
                    let totalX = this.clientWidth,
                        clickX = event.offsetX;
                    if (!is_single_video) {
                        if (clickX < totalX / 2) { // 打开左边的视频
                            window.open($(".cont-item-1 #kmd-video-player").attr("src"));
                        } else { // 打开右边的视频
                            window.open($(".cont-item-2 #kmd-video-player").attr("src"));
                        }
                    } else { // 只有一个视频
                        window.open($(".cont-item-1 #kmd-video-player").attr("src"));
                    }
                    event.preventDefault();
                    return false;
                })
            }

            let video_check_interval = setInterval(function () {
                if ($("video").length && $("#timesContorl").length && kmplayer.ids.length && $(".kmd-wrapper #kmd-video-player")[0]) { // 直到video元素和#timesContorl元素和视频生成,大概能表明播放器完全加载出来了
                    clearInterval(video_check_interval);
                    work();
                } else {
                    console.log("loading...");
                }
            }, 10);

            // 将全局右键屏蔽改为仅应用至视频区域
            $("#rtcMain").bind('contextmenu', function (event) {
                // console.log("contextmenu");
                event.preventDefault();
                return false;
            });

            // 禁止标题文字被选中,改善体验……算了不改了,没改善。
            // $(".list-title").css("user-select","none");

            // 本插件顺利生效时,顶部标签卡颜色为金色
            $(".tab-item--active").css("color", "gold");
            $(".tab-item--active").css("border-bottom", "2px solid gold");

            // 将画面背景色由廉价的灰色改为圣洁白
            $(".lti-page").css("background-color", "white");

            // 通过增加边框使右侧视频列表更有质感
            $(".lti-list").css("border", "2px solid black")
                .css("border-radius", "10px");
            $(".list-title").css("border-bottom", "1px solid black");
            $(".lti-list>div").css("background-color", "transparent");

            // 去除了画面中不必要的滚动条
            $(".lti-list").height("480px");
            $(".list-main").height("420px");
            $(".course-details").height(0);

            // 阻止向服务器回报观看日志
            updateVodPlayLog = updateLiveCount = addVodPlayLog = function () {};

            console.log("录播加强已启动!");
        });
    }

    // 稍微也改一下 vshare
    if (is_vshare_page) {
        if ($(".video-wrapper").length) {
            let wait_video_interval = setInterval(function () {
                if (!$("#video-share_html5_api").attr("src")) {
                    return;
                }
                clearInterval(wait_video_interval);

                // 恢复页面对基本操作的响应
                let document = window.document;
                document.onkeydown = document.oncontextmenu = undefined;
                document.body.oncontextmenu = document.body.onselectstart = undefined;

                // 替换播放器
                let v_src = $("#video-share_html5_api").attr("src");
                $("#video-share_html5_api")[0].pause(); // 终止原视频的播放
                $("#video-share_html5_api")[0].src = ""; // 终止原视频的播放
                $(".video-wrapper").replaceWith($('<video src="' + v_src + '"id="new_player" controls></video>'));
                $("#new_player").css("height", "100%").css("width", "100%");
            }, 10);
        }
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址