// ==UserScript==
// @name Canvas 平台录播播放器翻天覆地彻头彻尾焕然一新加强插件
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @description 优化上海交通大学 Canvas 平台录播观看功能
// @author danyang685
// @match https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage*
// @match https://courses.sjtu.edu.cn/lti/app/lti/liveVideo/index.d2j
// @match https://oc.sjtu.edu.cn/login/canvas
// @match https://oc.sjtu.edu.cn/courses/*/external_tools/162
// @match https://courses.sjtu.edu.cn/app/vodvideo/vodVideoPlay.d2j*
// @icon data:image/png;base64,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
// @grant none
// @license MIT
// ==/UserScript==
// 功能清单:
// [x] 到达 Canvas 登录(不可用)页时,自动跳转到 jAccount 登录(不可用)页
// [x] 在 Canvas 课程页面访问【课程视频】时,默认进入【点播】功能
// [x] 倍速列表选项优化(0.5-16倍)
// [x] 将全局右键屏蔽改为仅应用至视频区域
// [x] 状态栏时间显示帧率优化(由1秒刷新加速为0.05秒刷新)
// [x] 加快状态栏功能弹窗隐藏速度
// [x] 允许鼠标点击音量条任意位置设定音量并解除静音
// [x] 状态栏显示倍速
// [x] 在调整倍速后自动关闭菜单
// [x] 空格键或单击画面暂停/播放
// [x] 双击画面全屏
// [x] 左右方向键进行时移(3秒)
// [x] Ctrl+左右方向键进行快速时移(15秒)
// [x] Ctrl+Shift+左右方向键进行超快速时移(59秒)
// [x] 修正视频变形问题(但导致了空边)
// [x] 将黑边修改为空白边
// [x] 鼠标位于进度条上或拖动进度条时浮窗显示时刻
// [x] 修复了暂停状态下改变进度条导致意外继续播放且进度条不再刷新的问题
// [x] 为小窗视频略微增加透明度
// [x] 修改默认音量设定为不静音,并移除静音说明
// [x] 本插件顺利生效时,顶部标签卡颜色为金色
// [x] 在右上角增加【在新标签页播放】按钮
// [x] 阻止向服务器回报观看日志
// 已知问题:
// [x] 全屏模式下无法显示出时刻浮窗
(function () {
'use strict';
// 到达 Canvas 登录(不可用)页时,自动跳转到 jAccount 登录(不可用)页
if (window.location.href == "https://oc.sjtu.edu.cn/login/canvas") {
window.location.replace("https://oc.sjtu.edu.cn/login/openid_connect");
}
// 在 Canvas 课程页面访问【课程视频】时,默认进入【点播】功能
if (window.location.href == "https://courses.sjtu.edu.cn/lti/app/lti/liveVideo/index.d2j") {
if (document.referrer == "https://oc.sjtu.edu.cn/") {
window.location.replace("https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage");
}
}
// 应用到录播页面
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");
if (is_canvas_vod_page) {
console.log("Canvas录播加强!");
$(document).ready(function () {
let progressBar_operating_flag = false;
function isPlaying() { // 是否正在播放
return $(".tool-btn__play").css("display") == "none";
}
function setPlay(status) { // 设置播放/暂停
if (status) {
kmplayer.play("play");
} else {
kmplayer.play("pause");
}
}
function togglePlay() { // 切换播放/暂停
setPlay(!isPlaying());
}
function setTime(time) { // 设定播放位置
kmplayer.timeUpdateFlag = false; // 它里面就这么写的
kmplayer.setKMediaRate(time);
}
function getTime() { // 读取播放位置
return kmplayer.allInstance.type1.currentTime();
}
function updateTimeText() { // 重写状态栏播放状态
if (kmplayer && !progressBar_operating_flag) {
kmplayer.setViewSenTime(getTime()); // 时间显示同步
kmplayer.addSpeedRate(getTime()); // 进度条同步
}
}
function getDuration() {
return kmplayer.durationSec;
}
// 覆盖已有设定,有一说一,光屏蔽F12有用吗,Chrome还可以 Ctrl+Shift+I 呢。
window.onkeydown = window.onkeyup = window.onkeypress = window.oncontextmenu = undefined;
let video_check_interval = setInterval(function () {
if ($("video").length) { // 直到video元素生成
clearInterval(video_check_interval);
// 双击画面全屏
$("video").on('dblclick', function () {
kmplayer.scrren(); // 这都能拼错?
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(); // 说不定有点用?
// 加快状态栏功能弹窗隐藏速度
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").children("ul"), $("#timesContorl"));
}
}, 1);
let timeControl_check_interval = setInterval(function () {
if ($("#timesContorl").length) { // 直到#timesContorl元素生成
clearInterval(timeControl_check_interval);
// 修改默认音量设定为不静音,并移除静音说明
kmplayer.voice("voice!");
$(".mute-tip").hide();
// 倍速列表选项优化(0.5-16倍)
let speed_choice = [0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 3.5, 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").children("ul").html(speed_choice_text);
$("#timesContorl").children("ul").children("#1").attr("class", "times-active"); // 默认1倍高亮
// 在调整倍速后自动关闭菜单
$("#timesContorl").bind("click", function (event) {
if (event.target.tagName == "LI") {
// 状态栏显示倍速
$("#timesContorl").children("span").html(event.target.id);
$("#timesContorl").children("ul").css("display", "none");
}
return true;
});
// 鼠标位于进度条上或拖动进度条时浮窗显示时刻
$("body").append('<div id="custom-progress-hover"><span style="position: absolute; top: 50%; transform: translateX(-50%) translateY(-50%);">12:00</span></div>');
$("#custom-progress-hover") // 进度条,百万大制作
.css("position", "absolute")
.css("text-align", "center")
.css("display", "none")
.css("background-color", "#B2141AEE")
.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");
$(".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", $(".tool-progress").offset().top - 45);
$("#custom-progress-hover").css("left", event.pageX - 37);
let progress_sec = parseInt(progress_ratio * getDuration());
$("#custom-progress-hover").children("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");
});
}
}, 1);
setInterval(function () {
// 状态栏时间显示帧率优化(由1秒刷新加速为0.05秒刷新)
updateTimeText();
// 为小窗视频略微增加透明度
$(".cont-item-2").find("#kmd-video-player").css("filter", "brightness(1) contrast(1) saturate(1) hue-rotate(0deg) opacity(0.7)");
$(".cont-item-1").find("#kmd-video-player").css("filter", "brightness(1) contrast(1) saturate(1) hue-rotate(0deg)");
}, 50);
// 修复了暂停状态下改变进度条导致意外继续播放且进度条不再刷新的问题
function repair_pause_playing() {
if (!isPlaying()) {
let last_timestamp = getTime();
setTimeout(function () {
if (!isPlaying()) {
if (getTime() != last_timestamp) {
setPlay(false);
// console.log("repairing...");
}
}
}, 20);
}
}
$("body").on("mouseup", function () {
progressBar_operating_flag = true;
repair_pause_playing();
});
$("body").on("mousedown", function (event) {
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 voicebar_height = $(".voice-max").height();
let voicebar_base = $(".voice-max").offset().top;
let volume_ratio = 1 - (click_height - voicebar_base) / voicebar_height;
volume_ratio = volume_ratio > 1 ? 1 : volume_ratio;
volume_ratio = volume_ratio < 0 ? 0 : volume_ratio;
volume_ratio *= 100;
kmplayer.volume = volume_ratio;
kmplayer.setVolume(volume_ratio);
$(".voice-rate").height(volume_ratio);
kmplayer.voice("muted");
console.log("new vol: ",volume_ratio);
}
});
$("body").on("keyup", repair_pause_playing);
$("body").bind("keydown", function (event) {
// console.log("keydown:",event);
// 左右方向键进行时移
// 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;
}
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;
}
default:
return true;
}
event.preventDefault();
return false;
});
// 将全局右键屏蔽改为仅应用至视频区域
$("#rtcMain").bind('contextmenu', function () {
// console.log("contextmenu");
event.preventDefault();
return false;
});
console.log("录播加强已启动!");
$(".lti-page-tab").append($('<a href="https://courses.sjtu.edu.cn/lti/app/lti/vodVideo/playPage" target="_blank"><input class="tab-help" type="button" value="在新标签页播放"></a>'));
// 本插件顺利生效时,顶部标签卡颜色为金色
$(".tab-item--active").css("color", "gold");
$(".tab-item--active").css("border-bottom", "2px solid gold");
// 阻止向服务器回报观看日志
updateVodPlayLog = updateLiveCount = addVodPlayLog = function () {};
});
}
})();