Greasy Fork

Greasy Fork is available in English.

上海交通大学 Canvas 平台课程视频播放器至尊版焕然一新插件

优化上海交通大学 Canvas 平台课程视频播放器的功能

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         上海交通大学 Canvas 平台课程视频播放器至尊版焕然一新插件
// @namespace    http://tampermonkey.net/
// @version      1.6.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,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAACPhJREFUWEfFl3mM1dUVx7/n3N/vzcIAI8x7Q2DezCBSVCLEMswMi7hFg7hExSVxqVZNWou1TZqa2jY10aRNGxNjtDYmtikVuxCtUWJjUYtxQWYcxaWWgbLMxgyzCgzM8t7vnm9zHx0LCMU2Tfr+/N3lfO653/O95wn+zz/5b+K3YFE8VnGoGBiZHJlpomUH8mXj+Qvb2sb+0/2+MMCWaWdMYTx2ljOtMaIcIomAk0g4KAZBiRV22CvbOIq/Lz3YNfRFYE4J8FbFvMkxxlYQnBsCqWdrAnajBEPjUcQQpCqK2Lt/fHpMPZOOs2AoUbGOZBzNpwI5KQABaanMzjcvyyjSqZBuo12lIuYNzy0ZbN929AmbKrJ1gNwA4DAEbRT2gDpXaE31A53vCVCAPf53QoAQvDmdXU7oAlH8qb23vaM2XXWJQecpuNUT5czxraNPtyVTs1rI2wCUC/DW4Un64KSDuTRdfCnI3rKByRvn45PcKQEKwTM1jYBVuny8qe7T3QfCoqaK6iu8WGt6anHn0P7RGnEua6SDoESp/TAfm7oKwBaC8lFjf8ezYV3LzJmllotuJniofqDzDwLY0RCfy0Dz9OxiqpxdrOPPLuztPTwx+Z0ZtbWa5+l0doGYLIDgbAJeAAGREOgR4SYBdlGi1oa+PR9OrN1cVVUSjblrKdbR0N/55kkBWmbOrPA5dyugO/IofqM4NezrurtHmtO1M8L9Q3GnAGUhxaQ0k9hd2EysVqiLIaxH4a75vDn32yX72trC8Nby2nIfscQLViWRvrqsZ0/7BMQxGWjK1FxD4jQhF1PQ6uheHkcyHIvcC8GVMLwhER5fvK9jW0hly2mnT9WikdTOffuGrgfYUllda5Q1IK+CyAYIHvPmnYNbeQSUB0mJOvrb194A+MKnCZLmaTOz1GgVFKMkVgngTO1h592lBG8E+KuRMvdEMJtN6XRZmRbXGvUy0KoFshfQl9v62z5OA1JSUX2PCO4FsF6V68y0AeBCAO+RKBL41xsG9u44FiBdtRKqeQ875MxdArFBmh6A8NsgXh8Z6Pj+hUCyJVN9qxhWi+JDQg4VNiZGITJJKD+r72/7YD3gqiuqfyCC6wV4yGBbBXK9QrfC/KB3LtvY1/7cZwDBWn2m707vuWHpYNfepsqa2ZLPJ9T4fggXuyRaXffp7o5PMD81nD70CIR1QjSFayCwAEQXwA+ocubkvsn3hHJ7f8aMdN6nQiXsIfGYWtKXlOoADqJEU3pdlErWBX0VruDtyjmZmLnVSYq/XtrVNVoou8zshaB/gsSfGwc6HiyUFBbFVtF/OwVzIGwj5TQACxXo0SR6xLvkfqd8uK6vc1eY35zJ3k3KHaTc1zjQvil8C9mpyVTfLEmyqX6ou7MA0JLJzjHIksV9Hc8Exwpe8G5F9RUU/FjANfX9nW9srqw6x3m9BSpZEJuDIYK6wmApgXgQg1BMBjlOES/Ex0Z5W4XrKPhJQ1/Hugk3DJ4iol3hugoA706vqveq1cE8QvAgzncz2a+Tcrc5vTKU05Z09qsC+SbAcVA+CD4AYMVxzkYQf4MUHqcnqfqa0P8RghemTUk9OnfnzvEwf0tFzYUADzYOdASLPgJAlWJN4ra8s9OLorFPcknqZgHuSmJ3eajbcE1FiZ/mxc4XSBnEZpMyBcCXIdwBSEziNYi9pKINRmuKYtub5N2zELQo3RNGmzUqo9snaUkY72/s6/qoAFC4b0vOFmInBcudj5/zLrkMIt9wJjfWDba1hnlBWDkf32vg2gi6hpCRYDwCzITymfrejtcKOkn3XatJ/E4i4ybOvUhgrdf4d47JeUiSJmg0l86PLOntCkIGggfAuYvzUbQpztt1ifMbI2iGHo9S5YGJkmlOZ28k5bCKeAO/RUEHKHkBM4DsoI9/Ojq0c2RSOnuLd+51SThbhL+A8HtMZAec1BblZfN4ihc7078uHtizvQAQmg11udvzkXsh8sllKrKT+WQ7XfRUMBkjfw7gchUU5yP3ZJT470gQW/ABhcBAqMxT4unyqfFfBg/krhT6j6DR10g2RKZ30dl0A+YIdSPByxONNizr3dVXACioPp29TRQt9DKbyllE9LxYchNE1gjlAQqvFmCeEY+rygDJuUJJAJRSMQZaCtB94vMbDaUHJMrPB7mWwt9Hpr8w4CLCTJ00GbG8va/jmWDHn1nxlsraRjWrDhCeci3gNwpiAf1DIEoVbg2cz9IsEtFhT9T982ktFUqKgiKC/aLyovMYS8R+Gdo0Ovuhg5bBc6Ugeso0OUtEhoNejrHi0HpFMnpH2IDkGSqwJLFWEVmkIt8FsF+V9yfiDknOSiVy8ZH3JWgwKFFoZi5SlhC4j8RcAj+KhFs9ZBkhh0XwPrxd41L+N3Xd3QPHABxxrpqlNKuxyL3jzK8i0I3Y3mQ+ukDINQJMJbCeHhus1PYs6eoaC+YSLHqkcrjKPK+G4CtAQZgPe+eakPgFqlIenM+rO1cUY419nRtP+BxvAqLSTPVNCuyC8ACJ8wzaZ95vUdEZKriFwHIBikj0iWKXsdAd1wKoBhie2NBwrPfG3aq6BAhe4d9kSI6z84sl9/TRjc7nOqItmdmVoF0nTF5xjNWcv4iGZovcgMvlPJ2rJWWRCM6FwAGiAg4TsjM0oIDbIZSpBn+BKDUhX3GkiurFOcMLywc6u0/aEU0MBAihX6lm20Rcp4fNomiDkh0Eeyg8oIiG85EUpfIozsUYk1w+iTRKa6JDFicrhDLoRd93hnLCrjD1rwTnO866/9WQHD+weXrVLKdupZB96qMPmcpPMepcktUKHjDwbaE7X8TC6TMgqkTZZqatCWx72C+CzhPwSzT/UuPQ3q7jY3xOhMdPCB2tT9x5Aswx020R0ArvYitKJh/2o+2TWLRInMYeekjFhuF1UBNN+djPovGc4BeWxK82Du08eKLgpwSYWBSaUpKLKJZRkbyZ7HWC/VDL50EJWoG36RCrFA0pR48k/r3FQ91dJ/tDcsIqOBnlxPfQXrsxZoloFmGTnTAncEIy8tDhCNLjzfX8uxN/YQ2cCuZ/Nf4P8iTQXa0LxcMAAAAASUVORK5CYII=
// @grant        GM_info
// @grant        GM_addStyle
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

/********************************************************************
功能清单:
[x] 将 vshare 网站的视频播放器替换为浏览器内置播放器
[x] 到达 Canvas 登录页时,自动跳转到 jAccount 登录页
[x] 允许在课前25分钟即开始观看课程直播
[x] 当距离上课还有超过40分钟时,访问【课程视频】自动切换到点播页面
[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] 使用.键进入超慢速定位模式,方便寻找合适的时间点(替代不易实现的下一帧功能)
[x] 使用键盘快捷键ASD进行默认播放速度调整
[x] 使用[Crtl]和数字1、2进行当前视频截图和保存
[x] 可以在小画面上使用鼠标滚轮缩放画面
[x] 修正视频变形问题(但导致了空边)
[x] 将黑边修改为空白边
[x] 鼠标位于进度条上或拖动进度条时浮窗显示时刻
[x] 修复了暂停状态下改变进度条导致意外继续播放且进度条不再刷新的问题
[x] 为小窗视频略微增加透明度
[x] 修改点播默认音量设定为不静音,并移除静音说明
[x] 移除了底部课程信息区域,压缩页面高度
[x] 在右上角增加【在新标签页播放】按钮
[x] 清空了视频下方的说明文字
[x] 使右侧视频列表的内容更紧凑,并增加了教室显示,并去除了时间显示的00秒部分
[x] 列表好长好长好长的时候,自动将滚动条定位到当前视频条目
[x] 在视频列表顶部文字中显示视频总数
[x] 将直播中同一场课程的多个节次合并显示
[x] 修复了右侧视频栏中已激活的视频在部分场景下仍然可点击的bug
[x] 使用配色糟糕的标签突出显示未观看过的视频
[x] 增加了画中画模式,并允许从设置中直接选择所需画面
[x] 仅有一路视频时,禁用了画面布局按钮
[x] 增加了双路视频手动同步功能,能够比较方便地进行画面同步
[x] 增加画面参数调整选项,可进行亮度、对比度、透明度的调节
[x] 支持注册 MediaSession 从而通过系统控制视频的播放与暂停
[x] 阻止向服务器回报观看日志
[x] 去除了页面中没有必要的滚动条
[x] 移除了缺乏使用场景的【停止播放】按钮
[x] 移除了直播页面中没有任何作用的【画质】按钮
[x] 自动更新cookie,防止页面会话失效
[x] 直播中没有电脑视频流时,以【仅现场画面】模式启动
[x] 从canvas内直接打开【视频点播】时,自动切换到上次观看的视频
[x] 打开视频后,自动跳转到上次观看的进度
[x] 打开视频后,自动载入上次的默认播放速度
[x] 为每个视频分别记忆时间同步参数,下次观看无需再设置
[x] 重新打开时,记忆上次的小画面尺寸
[x] 不同canvas用户之间,个人观看偏好不互通
[x] 允许通过点击顶部当前标签页重新载入当前网页
[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_canvas_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.startsWith("https://courses.sjtu.edu.cn/lti/app/lti/liveVideo/index.d2j");
    // 课程视频 LTI 插件页面
    let is_canvas_lti162_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");
    }

    // LTI插件页面
    else if (is_canvas_lti162_page) {
        $(document).ready(function () {
            // 去除了页面中多余的滚动条
            let clear_resize_event_interval = setInterval(function () {
                $(window).off("resize"); //删除疑似画蛇添足的resize事件,瞎删除1百次就行吧
                $(".tool_content_wrapper").attr("style", "");
                $("#tool_content").css("min-height", "545px");
            }, 100);
            setTimeout(() => clearInterval(clear_resize_event_interval), 10000);

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

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

            // 自动更新cookie,防止页面会话失效
            function help_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") {
                    console.log("收到子页面的message", data);

                    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!": {
                            help_refresh_session();
                            break;
                        }
                        case "helpr!": {
                            help_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");
                                console.log("子页面要求在更新会话后刷新,刷新。");
                            }
                            break;
                        }
                    }
                }
            })
        })
    }


    // 课程视频页面
    else if (is_canvas_live_page || is_canvas_vod_page) {
        console.log("%cCanvas课程视频加强!%c当前链接:%c" + window.location.href, "color:blue;", "color:green;", "color:#0FF;");

        // 创建截图
        function makeScreenshot(video_id, no_download = false) {
            let se = $(".cont-item-" + video_id + " #kmd-video-player");
            if (se) {
                let el = se[0];
                console.log(el);
                let t = el.currentTime;
                // 抄的jAccount验证码识别那个插件
                $("body").append($('<canvas width="1280" height="720" style="display: none;" id="screenshot_canvas"></canvas>'));
                let canvas_el = document.getElementById("screenshot_canvas"),
                    current_time = ("00" + parseInt(t / 60 % 60)).slice(t >= 60 * 100 ? -3 : -2) + "_" + ("0" + parseInt(t % 60)).slice(-2) + "_" + ("0" + parseInt(t * 100 % 100)).slice(-2);

                console.log(current_time);
                canvas_el.getContext("2d").drawImage(el, 0, 0);
                let screenshot_img = canvas_el.toDataURL("image/jpeg");
                $("#screenshot_canvas").remove();
                if (no_download) { // 在新标签页打开,而不是下载
                    //console.log(screenshot_img);
                    window.open("", null, "width=1330,height=770,status=yes,toolbar=no,menubar=no,location=no").document.body.innerHTML = '<img src="' + screenshot_img + '">';
                } else { // 下载
                    $("body").append($('<a href="' + screenshot_img + '" id="download_link" download="canvas_screenshot_' + current_time + '.jpg">下载</a>'));
                    $("#download_link")[0].click(); // 哦是我之前在浏览器里阻止了下载
                    $("#download_link").remove();
                }
            }
        }

        let auto_refresh_flag = false,
            allow_long_live_flag = false, // 嘿嘿,嘿嘿
            allow_live_earlier = false,
            vlist_ajax_called_flag = true;
        canvas_live_vod_enhance_pre();
        let video_list_loaded_flag = false; // 通过捕获 getVideoInfoById 的调用来进行视频加载状态的检查
        {
            let raw_getVideoInfoById = getVideoInfoById;
            getVideoInfoById = function (id) {
                let vlist_ajax_called_flag_waiting_interval = setInterval(function () {
                    if (!vlist_ajax_called_flag) {
                        return; // 哦原来它没有返回值啊,那太好了,可以用俺的土办法嘿嘿嘿
                    }
                    clearInterval(vlist_ajax_called_flag_waiting_interval);

                    let data = undefined;
                    let newfunc_getVideoInfoById = undefined;
                    eval("newfunc_getVideoInfoById = " + raw_getVideoInfoById.toString().replace("getVideoInfoById", "").replace("noStart();\n\t\t\t\t\treturn;", ""));

                    /*
                    while(!vlist_ajax_called_flag){
                        (async () => await new Promise(resolve => setTimeout(resolve, 10)))(); // 抄来的高级代码啊哈哈哈哈,嘤嘤嘤根本等不来那个ajax callback
                        console.log("waiting for vlist_ajax_called_flag");
                    }
                    */

                    if (allow_live_earlier) {
                        raw_getVideoInfoById = newfunc_getVideoInfoById;
                        console.log("allow_live");
                    }
                    raw_getVideoInfoById(id);
                    video_list_loaded_flag = true; // 如果没有视频的时候,它就不会被调用了emmmm。。。
                    console.log("loaded!");
                }, 100);
            }

        }
        let onload_check_interval = setInterval(function () {
            if (!video_list_loaded_flag) { // 或许用 $("body .loading").css("display")!="none" 判断的话会有极小概率出现线程间不同步(或许??)
                console.log("loading...");
                return;
            }
            clearInterval(onload_check_interval);
            canvas_live_vod_enhance();
        }, 50);

        function canvas_live_vod_enhance_pre() {
            if (is_canvas_vod_page) {
                // 阻止向服务器回报观看日志
                if (window.addVodPlayLog) addVodPlayLog = function () {};
                else {
                    console.log("%c日志阻断失败", "color:#0FF;");
                }
                if (window.updateLiveCount) updateLiveCount = function () {};
                else {
                    console.log("%c日志阻断失败", "color:#0FF;");
                }
                if (window.updateVodPlayLog) updateVodPlayLog = function () {};
                else {
                    console.log("%c日志阻断失败", "color:#0FF;");
                }
                //dateVodPlayLog = updateLiveCount = addVodPlayLog = function () {};
            } else {
                if (window.updateLivePlayLog) updateLivePlayLog = function () {};
                if (window.updateLiveCount) updateLiveCount = function () {};
                if (window.addLivePlayLog) addLivePlayLog = function () {};
                //dateLivePlayLog=updateLiveCount=addLivePlayLog = function () {};
            }

            // 允许通过点击顶部当前标签页重新载入当前网页
            $(".tab-item--active").click(function () {
                window.location.replace(window.location.href);
            });

            if (is_canvas_live_page) {
                // 将直播中同一场课程的多个节次合并显示
                let old_creatCourseList_func = creatCourseList;
                creatCourseList = function (lst) {
                    let joint_live_list = Array.from(function* getJointLiveItemList(lst) {
                        // 注意一会别忘了写长度为1的判定。……啊哈根本不需要写哈哈哈哈哈
                        for (let i = 1; i < lst.length; i++) {
                            let is_continious = Date.parse(lst[i].courBeginTime) - Date.parse(lst[i - 1].courEndTime) <= 1000 * 60 * 20, // 间隔小于20分钟
                                is_same_room = lst[i].clroName == lst[i - 1].clroName; // 在同一教室
                            if (is_continious && is_same_room) { // 视为同一课程安排
                                lst[i - 1].courEndTime = lst[i].courEndTime;
                                lst[i] = lst[i - 1];
                                console.log("joint!")
                            } else {
                                yield lst[i - 1]; // 之前那个
                            }
                        }
                        yield lst[lst.length - 1]; // 最后一个
                    }(lst));
                    old_creatCourseList_func(joint_live_list);
                }
            }
            $(document).ajaxComplete(function (event, xhr, settings) { // woc jQuery好厉害,但它不够快。明天再改。。。。。。。
                switch (settings.url) {
                    case "/lti/liveVideo/findLiveList": {
                        //console.log("ajax done!!!",event,xhr,settings);
                        if (xhr.status != 200) { // 这是因为后台页面正在进行登录
                            window.location.reload();
                        }
                        let live_list = xhr.responseJSON.body.list;
                        if (live_list.length) {
                            let this_id = (new URLSearchParams(window.location.search).get("id") || live_list[0].id).replaceAll(" ", "+"); // 为什么加号会变成空格??
                            console.log("活跃的视频ID:" + this_id);
                            live_list = live_list.filter(live_item => live_item.id == this_id);
                            if (live_list.length) {
                                let course_start_time_span = (Date.parse(live_list[0].courBeginTime) - new Date().getTime()) / 1000 / 60;
                                // 允许在课前25分钟即开始观看课程直播
                                if (course_start_time_span < 25 || allow_long_live_flag) {
                                    allow_live_earlier = true;
                                    vlist_ajax_called_flag = true; // 否则是来不及的
                                    console.log("try_allow_live");
                                }
                            }
                        } else {
                            video_list_loaded_flag = true; // 直接置位视频列表已加载的标志
                        }
                        break;
                    }
                    case "/lti/vodVideo/findVodVideoList": {
                        //console.log("ajax done!!!",event,xhr,settings);
                        if (xhr.status != 200) { // 这是因为后台页面正在进行登录
                            window.location.reload();
                        }
                        let vod_list = xhr.responseJSON.body.list;
                        if (vod_list.length == 0) {
                            video_list_loaded_flag = true; // 直接置位视频列表已加载的标志
                        }
                        break;
                    }
                    default: {
                        break;
                    }
                }
            });
        }

        function canvas_live_vod_enhance() {
            let is_from_default_lti_entry = document.referrer == "https://oc.sjtu.edu.cn/",
                video_count = $(".lti-list .item-text").length || $(".lti-list .item-infos").length,
                is_live_playing = $(".lti-list .live-course-item--avtive .icon-play").length,
                no_live_video_played = false;

            let is_single_video = undefined,
                master_done_flag = 0,
                use_storage_flag = true;


            // 在视频列表顶部文字中显示视频总数
            $(".lti-list>.list-title").append($('<span style="font-size: 50%;">(' + video_count + '条视频)</span>'));


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

            function getStorage(k) {
                if (use_storage_flag) {
                    return localStorage.getItem("canvasnb_plugin_" + k);
                } else {
                    return null;
                }
            }

            // 首先获取本页面的课程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) {
                // august反馈,他的页面一直在疯狂刷新(研究生课程),故对该情况禁用记忆功能,反正啥也记不住
                // window.location.reload();
                use_storage_flag = false;
                course_id = "UNKNOWN_COURSE"
            }

            // 再获取本视频的视频ID
            let video_id = $(".lti-list .list-item--active").attr("id") || $(".lti-list .live-course-item--avtive").attr("id");

            // 修复了右侧视频栏中已激活的视频在部分场景下仍然可点击的bug
            if (video_id && video_id.length > 10) { // 10是我瞎写的
                let net_url = window.location.origin + window.location.pathname + "?id=" + video_id;
                console.log("raw url: " + window.location.href + " , push new url: " + net_url);
                window.history.pushState('data', document.title, net_url); // 点播用的这种实现
                if (is_canvas_live_page) {
                    urlId = video_id; // 直播用的这种实现
                }
            }

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

            // 跳转到合适的直播页面
            function redirectToVodPage() {
                // 修复了自动跳转到点播页面时,需要二次跳转到上次视频的bug
                let last_play = getStorage(userid + "_course_" + course_id + "_lastplay_video_id");
                if (last_play != null) {
                    window.location.replace("https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage?id=" + last_play);
                }
            }

            if (is_canvas_live_page) {
                let should_redirect_flag = false;
                if (video_count != 0) {
                    let item_date = $(".live-course-item--avtive .item-infos p:nth-child(2) span:nth-child(2)").text(),
                        item_time = $(".live-course-item--avtive .item-infos p:nth-child(3) span:nth-child(2)").text().split("~")[0],
                        next_date_span_minute = (Date.parse(item_date + " " + item_time) - new Date().getTime()) / 1000 / 60; // 距离正式开始直播的分钟数

                    // 当距离上课还有超过40分钟时,访问【课程视频】自动切换到点播页面
                    if (next_date_span_minute > 40 && !allow_long_live_flag) {
                        should_redirect_flag = true;
                    }
                } else {
                    should_redirect_flag = true;
                }
                if (is_from_default_lti_entry) {
                    if (window.innerHeight == 0) { // 我是用于更新会话的工具iframe
                        console.log("会话更新已完成!课程ID:%c" + course_id, "color:#0FF;");
                        top.postMessage("done!", "https://oc.sjtu.edu.cn");
                        return;
                    }
                    if (should_redirect_flag) {
                        redirectToVodPage();
                    }
                }
            }

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

            console.log("视频ID:" + video_id);

            if (is_iframe && (document.referrer == "https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage" || document.referrer == "https://oc.sjtu.edu.cn/")) {
                console.log("发送message:online!");
                top.window.postMessage("online!", "https://oc.sjtu.edu.cn");
            } else {
                master_done_flag += 1; // 这个是跨页面通讯的完成标志
            }

            console.log("配置message");
            $(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发送消息
                    console.log("收到父页面的message", data);
                    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"]);
                        setStorage("course_" + course_id + "_last_username", message["user_name"]);
                        userid = message["user_id"];
                        username = message["user_name"];
                        master_done_flag += 1; // 这个是跨页面通讯的完成标志
                        console.log("config_message: ", message);
                    } else if (message["message_type"] == "request_refresh") {
                        location.reload(); // 刷新页面
                    }
                }
            });

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

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

            // 移除直播视频上的两个无意义图标
            $(".live-review-icon").remove();
            $(".icon-play").remove();


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

            // 在右上角增加【在新标签页播放】按钮
            if (is_iframe) { // 【在新标签页播放】时不再显示【在新标签页播放】按钮
                $(".lti-page-tab").append($('<button class="tab-help" onclick="window.open(window.location.href)" id="btn_play_in_new_tab">在新标签页播放</button>'));
            }
            if (is_canvas_vod_page) {
                $("#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;
                })
            }

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

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

                // 加快状态栏功能弹窗隐藏速度
                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"));

                // 增加画面参数调整选项,可进行亮度、对比度、透明度的调节
                $('<div id="effectControl" class="tool-bar-item tool-btn__times" style="position: relative; z-index: 20;"><span>画面</span><div><p>画面设定</p></div></div>')
                    .insertAfter("#splitContorl");

                $("#effectControl")
                    .css("position", "relative")
                    .css("z-index", "20");

                $("#effectControl>div")
                    .css("position", "absolute")
                    .css("display", "none")
                    .css("bottom", "25px")
                    .css("left", "-58px")
                    .css("height", "250px")
                    .css("width", "150px")
                    .css("border-radius", "3px")
                    .css("background-color", "rgba(28, 32, 44, .9)");
                $("#effectControl>div>p")
                    .css("font-size", "150%")
                    .css("font-weight", "bold");

                let effect_control_list = [{
                    for: "opacity",
                    label: "小窗透明度",
                    default: 0.85,
                    min: 0.2,
                    max: 1.0,
                    target_action: (num) => {
                        effect_setting.opacity = num;
                    }
                }, {
                    for: "brightnesssA",
                    label: "现场亮度",
                    default: 1,
                    min: 0.7,
                    max: 1.5,
                    target_action: (num) => {
                        effect_setting.brightnesssA = num;
                    }
                }, {
                    for: "brightnesssB",
                    label: "电脑亮度",
                    default: 1,
                    min: 0.7,
                    max: 1.5,
                    target_action: (num) => {
                        effect_setting.brightnesssB = num;
                    }
                }, {
                    for: "contrastA",
                    label: "现场对比度",
                    default: 1,
                    min: 0.8,
                    max: 2.5,
                    target_action: (num) => {
                        effect_setting.contrastA = num;
                    }
                }, {
                    for: "contrastB",
                    label: "电脑对比度",
                    default: 1,
                    min: 0.8,
                    max: 2.5,
                    target_action: (num) => {
                        effect_setting.contrastB = num;
                    }
                }]

                let effect_setting = JSON.parse(getStorage(userid + "_effect"));
                if (!effect_setting) {
                    effect_setting = Object.fromEntries(effect_control_list.map(x => [x.for, x.default])); // 哦越来越熟练了呢
                }

                setStorage(userid + "_effect", JSON.stringify(effect_setting));

                effect_control_list.forEach(function (v) {
                    if (effect_setting[v.for] == undefined) {
                        effect_setting[v.for] = v.default; // 其实上面那句可以删了,嘿但我就不删,看着多高级啊
                    }
                    $("#effectControl>div").append($('<div id="effect_container_' + v.for+'"></div'));
                    let container = $("#effectControl>div>#effect_container_" + v.for);
                    container.append($("<label>" + v.label + "</label>"));
                    container.append($('<input type="range" id="effect_slider_' + v.for+'" min="0" max="100">')); //嘤嘤嘤slider的默认样式好美啊
                    let range_slider = $("#effectControl>div #effect_slider_" + v.for);

                    range_slider[0].value = 100 * (effect_setting[v.for] - v.min) / (v.max - v.min);
                    v.target_action(effect_setting[v.for]);
                    range_slider.on("input", function (event) {
                        let new_value = event.target.value / 100;
                        v.target_action(v.min + (new_value * (v.max - v.min)));
                        setStorage(userid + "_effect", JSON.stringify(effect_setting));
                    });
                })
                $("#effectControl>div").append('<input type="button" id="btn_reset_effect" value="还原默认设定">')
                $("#btn_reset_effect").on("click", function () {
                    effect_control_list.forEach(function (v) {
                        effect_setting[v.for] = v.default;
                        let range_slider = $("#effectControl>div #effect_slider_" + v.for);
                        range_slider[0].value = 100 * (effect_setting[v.for] - v.min) / (v.max - v.min);
                        v.target_action(effect_setting[v.for]);
                    })
                });

                new_hoverleave_callback($("#effectControl>div"), $("#effectControl"));

                // 有两路视频时,能够通过设定参考点的方式进行画面同步
                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"));
                }

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

                // 使右侧视频列表的内容更紧凑了,并增加了教室显示
                let this_teacher;
                $(".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: -40px;left: 160px;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());

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


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

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

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

                // 设定播放速度
                function setSpeed(speed) {
                    console.log("设定速度:" + 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);
                    // 新方案才能实现精准的时间
                    //kmplayer.allInstance.type1.currentTime(getTime());
                    //kmplayer.allInstance.type2.currentTime(getTime() + getTimeDelta());
                    // 但是这样就开始自动播放了。。。
                }

                // 读取播放位置
                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 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不生效,为什么呢?
                }

                // 打开后默认不自动播放视频
                console.log("is vod:" + is_canvas_vod_page);
                if (is_canvas_vod_page) {
                    let auto_pause_interval = setInterval(function () {
                        if (getTime() > 0.1) { // 这个判据不合适吗?为什么有时候会无法成功暂停?
                            console.log(getTime());
                            clearInterval(auto_pause_interval);
                            setPlay(false, true);
                            setTimeout(function () {
                                // 打开视频后,自动跳转到上次观看的进度
                                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);
                                    }
                                }
                                setTimeout(function () {
                                    setPlay(false, true);
                                }, 100);
                                // 打开视频后,自动载入上次的默认播放速度
                                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);
                                }

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

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

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

                // 修正视频变形问题(但导致了空边)
                $("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(); // 说不定有点用?



                // 支持注册 MediaSession 从而通过系统控制视频的播放与暂停
                if ('mediaSession' in navigator && is_canvas_vod_page) {
                    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)
                        console.log("MediaSession请求播放!");
                    });
                    navigator.mediaSession.setActionHandler('pause', function () {
                        setPlay(false)
                        console.log("MediaSession请求暂停!");
                    });
                    navigator.mediaSession.setActionHandler('stop', function () {
                        kmplayer.stop("stop")
                        console.log("MediaSession请求停止!");
                    });
                    navigator.mediaSession.playbackState = "paused";
                }

                // 当视频链接过期时,自动刷新链接
                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);
                        }
                    });
                }
                $(".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");
                        }
                    }
                })

                let periodic_job_interval = setInterval(periodic_job, 100); // 100ms 的定时任务
                function periodic_job() {
                    if (!kmplayer.allInstance.type1) { // 检查直播状态是否已经结束(否则会导致下方过程中出错)
                        clearInterval(periodic_job_interval);
                    }
                    // 状态栏时间显示帧率优化(由1秒刷新加速为0.05秒刷新)
                    updateTimeText();

                    let screen_switched = $(".cont-item-1 .kmd-app-container").attr("id") != "player-00001";
                    let brightnesssA = screen_switched ? effect_setting.brightnesssA : effect_setting.brightnesssB;
                    let brightnesssB = !screen_switched ? effect_setting.brightnesssA : effect_setting.brightnesssB;
                    let contrastA = screen_switched ? effect_setting.contrastA : effect_setting.contrastB;
                    let contrastB = !screen_switched ? effect_setting.contrastA : effect_setting.contrastB;

                    // 为小窗视频略微增加透明度
                    $(".cont-item-2 #kmd-video-player").css("filter", "brightness(" + brightnesssA + ") contrast(" + contrastA + ") saturate(1) hue-rotate(0deg) opacity(" + effect_setting.opacity + ")");
                    $(".cont-item-1 #kmd-video-player").css("filter", "brightness(" + brightnesssB + ") contrast(" + contrastB + ") saturate(1) hue-rotate(0deg)");

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

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

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


                // 倍速列表选项优化(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();
                // 移除了直播页面中没有任何作用的【画质】按钮
                $(".tool-btn__sharp").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)");
                }

                // 切换画中画设定
                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,
                    split_changed = 0;
                $("#splitContorl").bind("click", function (event) {
                    if (event.target.tagName == "LI") {
                        split_changed++;
                        $(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").attr("class", $(event.target).attr("class")); // 虽然网页里已经定义过这个事件,但是好像有些晚,所以我在这重复操作一遍。
                    }
                });

                // 直播中没有电脑视频流时,以【仅现场画面】模式启动
                if (is_canvas_live_page) {
                    $("#split_scene_only").click();
                    let computer_video = $(".cont-item-2 #kmd-video-player");
                    computer_video.one("playing", function () {
                        console.log("playing....", 321);
                        if (split_changed == 2) {
                            $("#split_big_small").click(); // 切换回一大一小模式
                        }
                    });
                    if (computer_video[0].currentTime > 0) { //防止刚才趁程序不注意就开始了播放
                        computer_video.trigger("playing");
                    }
                }

                // 鼠标位于进度条上或拖动进度条时浮窗显示时刻
                $("#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%)");

                // 浮窗显示
                $(".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)-")); // 这里改成另一个格式的RegExp之后要转义反斜杠了
                        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;
                });

                let in_ultra_slow_mode = false;
                $("body").on("keyup", function (event) {

                    let is_using_default_speed = (Math.abs(getSpeed() - default_time_speed) < 0.005);
                    // console.log("keyup:",event.target);
                    switch (event.keyCode) {
                        case 37: // 方向键左
                        case 39: { // 方向键右
                            repair_pause_playing();
                            break;
                        }
                        case 190: { // >键
                            // 使用.键进入超慢速定位模式,方便寻找合适的时间点(替代不易实现的下一帧功能)
                            if (is_using_default_speed) {
                                setSpeed(default_time_speed); // 恢复到之前的播放速度
                            } else {
                                setSpeed(current_time_speed);
                            }
                            setPlay(false, true);
                            in_ultra_slow_mode = false;
                            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 49: { // 数字1,左屏幕截图
                            makeScreenshot(1, event.ctrlKey);
                            break;
                        }
                        case 50: { // 数字2,右屏幕截图
                            makeScreenshot(2, event.ctrlKey);
                            break;
                        }
                        case 190: { // >键
                            // 使用.键进入超慢速定位模式,方便寻找合适的时间点(替代不易实现的下一帧功能)
                            if (!is_canvas_vod_page) break; //仅针对点播
                            if (!in_ultra_slow_mode) {
                                in_ultra_slow_mode = true;
                                setSpeed(0.1); // 这是它支持的最低速度
                                if (!isPlaying()) {
                                    setPlay(true, true);
                                }
                            }
                            putText("超慢速播放中");
                            break;
                        }
                        case 39: { // 方向键右
                            if (!is_canvas_vod_page) break; //仅针对点播
                            let target_time = getTime() + step;
                            target_time = target_time > kmplayer.durationSec ? kmplayer.durationSec : target_time;
                            setTime(target_time);
                            // console.log("add:", step, "to:", target_time);
                            break;
                        }
                        case 37: { // 方向键左
                            if (!is_canvas_vod_page) break; //仅针对点播
                            let target_time = getTime() - step;
                            target_time = target_time < 0 ? 0 : target_time;
                            setTime(target_time);
                            // console.log("minus:", step, "to:", target_time);
                            break;
                        }
                        case 67: { // 字母C
                            if (!is_canvas_vod_page) break; //仅针对点播
                            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
                            if (!is_canvas_vod_page) break; //仅针对点播
                            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_canvas_vod_page) break; //仅针对点播
                            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
                            if (!is_canvas_vod_page) break; //仅针对点播
                            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
                            if (!is_canvas_vod_page) break; //仅针对点播
                            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
                            if (!is_canvas_vod_page) break; //仅针对点播
                            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("mousewheel", function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                    return false;
                });

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

                    event.preventDefault();
                    event.stopPropagation();
                    // 仅在视频上使用滚轮生效
                    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));
                    }
                    return false;
                });
            }

            let video_check_interval = setInterval(function () {
                if ($("video").length && kmplayer.ids.length && $(".kmd-wrapper #kmd-video-player")[0]) { // 直到video元素和#timesContorl元素和视频生成,大概能表明播放器完全加载出来了
                    clearInterval(video_check_interval);
                    work_all();
                    master_done_flag += 1; // 这个是这个任务的完成标志
                } else {
                    if ($(".not-data").length || no_live_video_played) {
                        clearInterval(video_check_interval);
                        console.log("无直播视频,退出检测");
                    }
                    console.log("loading...");
                }
            }, 50);

            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 && auto_refresh_flag) {
                        refresh_session();
                    }
                })
                setInterval(refresh_session, 20 * 60 * 1000); // 20分钟更新一次,防止页面失效
            }

            // 禁止标题文字被选中,改善体验……算了不改了,没改善。
            // $(".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("460px");
            $(".list-main").height("400px");

            // 移除了底部课程信息区域,压缩页面高度
            $(".course-details").height(0);

            // 将播放控制栏底色由廉价的灰色改为至尊黑
            GM_addStyle("#rtcTool .tool-bar {background-color: black;}");
            GM_addStyle("#rtcContent {background-color: black;}");

            console.log("%c施工完毕,辛苦了!", "color:#0FF;");
        }
    }

    // 将 vshare 网站的视频播放器替换为浏览器内置播放器
    else 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);
        }
    }
})();