- // ==UserScript==
- // @name 云班课高效助手
- // @name:zh-CN 云班课高效助手
- // @author bellamy.n.h
- // @namespace http://tampermonkey.net/
- // @version 1.86
- // @description 【高效再升级😃!高效使用云班课,一个脚本就够了!😎】 【🧡视频倍速:新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【💛视频连播:新版视频连播功能,支持从当前视频开始连播(配合视频控件,体验更佳)】、【💙快捷键:新增快捷键系统,常用功能都已加入,高效更进一步】、【💚资源处理:批量点击、下载、批处理】
- // @match https://www.mosoteach.cn/web/index.php*
- // @include *://www.mosoteach.cn/web/index.php*
- // @note Version 1.85 —— 1.86 修复连播视频时数量错误BUG;重构快捷键视图生成代码,降冗余;Add Statistical Analysis System;限制对快捷键的频繁操作;特殊处理部分高频使用的快捷键。
- // @note Version 1.80 😁【新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【新版视频连播功能,支持从当前视频开始连播(配合视频控件,可达到极度自由)】、【新增快捷键系统,常用功能已都加入,高效更进一步】、【修复模拟点击/下载失效Bug】、【限制全部连播最大速度为8倍】
- // @note Version 1.70 视频最高16倍速连播;调用系统通知,反馈更佳;
- // @note Version 1.65 偷偷改了些小Bug 🤭,使连播更顺畅。下个版本上16倍速连播喽😊
- // @note Version 1.60 新增测试功能,支持 连续播放所有视频、 立即看完当前视频(测试阶段,还请反馈)
- // @note Version 1.50 加强对输入值约束; 支持多栏处理; chrome浏览器自动打开 设置页面地址更改; 其他Bug修复。
- // @note Version 1.40 优化代码; 新增浏览器类型判断,支持chrome浏览器自动打开 设置页面。
- // @note Version 1.32 优化操作反馈 (可以重置已选择的资源栏数)
- // @note Version 1.31 修复可能存在的Bug (页面无法自动关闭)
- // @icon https://s1.ax1x.com/2020/05/18/Yf6Kcd.png
- // @require https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/layer/2.3/layer.js
- // @require https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
- // @require https://cdn.jsdelivr.net/npm/qs@6.9.4/dist/qs.min.js
- // @grant GM_openInTab
- // @grant GM_notification
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_deleteValue
- // @grant GM_listValues
- // ==/UserScript==
-
-
- $(function () {
- 'use strict';
-
- var config = {
- isCRX: false,
- notificationTitle: "云班课高效助手",
- icon128: "https://s1.ax1x.com/2020/05/18/Yf6pp4.png",
- icon48: "https://s1.ax1x.com/2020/05/18/Yf6Kcd.png",
- icon32: "https://s1.ax1x.com/2020/05/18/Yf6BBq.png",
- icon16: 'https://s1.ax1x.com/2020/05/18/Yfg71e.png',
- layer_css: "https://cdn.jsdelivr.net/npm/layui-layer@1.0.9/layer.min.css",
- layer_js: "https://cdnjs.cloudflare.com/ajax/libs/layer/2.3/layer.js",
- jquery_js: "https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js",
- layui_js: "https://cdn.jsdelivr.net/npm/layui-src@2.5.5/dist/layui.min.js",
- fontawesome_css: "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css",
- base: "https://mst.bellamy.top:8443",
-
- };
-
- var openInTab;
- var setVal;
- var getVal;
- var notification;
- var delVal;
- var listVals;
- var qs = Qs;
-
- if (config.isCRX) {
- console.log("in CRX");
- /**
- * ***********
- * For CRX Begin
- * ***********
- *
- * ***********
- * Override the following apis provided by TamperMonkey
- * these apis can only work well in TamperMonkey Script
- * but they can not work in CRX
- */
-
- /**
- * [GM_openInTab : send message to bg.js to create New Tab according to these following parameters]
- * @param {[String]} forWhat [onDownload or offDownload]
- * @param {[String]} _url [new tab]
- * @param {[Boolean]} _option [is or not active]
- */
- function GM_openInTab(_url, _option, forWhat) {
-
- chrome.runtime.sendMessage({
- createTab: forWhat,
- url: _url,
- option: "active" === _option,
- });
-
- }
-
- function GM_setValue(name, value) {
-
- }
-
- function GM_getValue(name, defaultValue) {
-
- }
- /**
- * send message to chrome API
- * chrome.notifications.create(string notificationId, NotificationOptions options, function callback)
- * @param {[type]} notificationDetails [description]
- * @param {Function} callback [description]
- */
- function GM_notification(notificationDetails, callback) {
- chrome.runtime.sendMessage({
- notifDetails: {
- details: notificationDetails,
- callbackFunc: callback
- }
- });
- }
-
- function GM_deleteValue(name) {
-
- }
-
- function GM_listValues() {
-
- }
-
- /**
- * ***********
- * For CRX End
- * ***********
- */
- openInTab = GM_openInTab; //GM_openInTab(url, option);
- setVal = GM_setValue; // GM_setValue(name, value)
- getVal = GM_getValue; // GM_getValue(name, defaultValue)
- notification = GM_notification; // GM_notification(text, title, image, onclick)
- delVal = GM_deleteValue; // GM_deleteValue(name)
- listVals = GM_listValues; // GM_listValues()
-
-
- // inject layer.css
- $("<link>")
- .attr({
- rel: "stylesheet",
- type: "text/css",
- href: config.fontawesome_css
- })
- .appendTo("head");
-
-
- } else {
- console.log("in Script ");
-
- openInTab = GM_openInTab; //GM_openInTab(url, option);
- setVal = GM_setValue; // GM_setValue(name, value)
- getVal = GM_getValue; // GM_getValue(name, defaultValue)
- notification = GM_notification; // GM_notification(text, title, image, onclick)
- delVal = GM_deleteValue; // GM_deleteValue(name)
- listVals = GM_listValues; // GM_listValues()
-
-
-
- // inject layer.css
- $("<link>")
- .attr({
- rel: "stylesheet",
- type: "text/css",
- href: config.layer_css
- })
- .appendTo("head");
-
-
- // inject layer.css
- $("<link>")
- .attr({
- rel: "stylesheet",
- type: "text/css",
- href: config.fontawesome_css
- })
- .appendTo("head");
-
- }
-
- /**
- * For notification function
- *
- * text - the text of the notification (required unless highlight is set)
- * title - the notificaton title
- * image - the image
- * highlight - a boolean flag whether to highlight the tab that sends the notfication (required unless text is set)
- * silent - a boolean flag whether to not play a sound
- * timeout - the time after that the notification will be hidden (0 = disabled)
- * ondone - called when the notification is closed (no matter if this was triggered by a timeout or a click) or the tab was highlighted
- * onclick - called in case the user clicks the notification
- */
- function getNotificationDetails(_text, _timeout, _title, _image, _highlight, _silent, _ondone, _onclick) {
-
- let details = {
- text: _text === undefined ? '' : _text,
- title: _title === undefined || _title === null ? config.notificationTitle : _title,
- image: _image === undefined || _image === null ? config.icon48 : _image,
- highlight: _highlight === undefined || _highlight === null ? true : _highlight,
- silent: _silent === undefined || _silent === null ? false : _silent,
- timeout: _timeout === undefined || _timeout === null ? 6000 : _timeout,
- ondone: _ondone === undefined || _ondone === null ? null : _ondone,
- onclick: _onclick === undefined || _onclick === null ? null : _onclick
- };
- return details;
-
- };
-
-
- /**
- * Determine the browser type
- */
- function browserType() {
- var userAgent = navigator.userAgent; //get browser userAgent string
- var isOpera = userAgent.indexOf("Opera") > -1;
- if (isOpera) {
- return "Opera"
- }; //is Opera or not
- if (userAgent.indexOf("Firefox") > -1) {
- return "FF";
- } //is Firefox or not
- if (userAgent.indexOf("Chrome") > -1) {
- return "Chrome";
- }
- if (userAgent.indexOf("Safari") > -1) {
- return "Safari";
- } //is Safari or not
- if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {
- return "IE";
- }; //is IE or not
- }
- /**
- * sleep function
- * @param numberMillis -- 要睡眠的毫秒数
- */
- function sleep(numberMillis) {
- var now = new Date();
- var exitTime = now.getTime() + numberMillis;
- while (true) {
- now = new Date();
- if (now.getTime() > exitTime)
- return;
- }
- }
-
- /**
- * Remove duplicate value in array
- */
- function removeDuplicate(arr) {
- let x = new Set(arr);
- return [...x];
- }
- /**
- * download resources function
- */
- function download(name, href) {
- var a = document.createElement("a"), //创建a标签
- e = document.createEvent("MouseEvents"); //创建鼠标事件对象
- e.initEvent("click", false, false); //初始化事件对象
- a.href = href; //设置下载地址
- a.download = name; //设置下载文件名
- a.dispatchEvent(e); //给指定的元素,执行事件click事件
- }
-
- /**
- * Refresh page tips
- */
- function refreshPage() {
- alert("操作完成,请小可爱刷新页面查看结果!!!");
- }
-
- /**
- * cancel action
- */
- function cancel() {
- alert("已取消操作!");
- }
- /**
- * 点击和下载前以弹窗二次确认
- * modeName
- * return boolean
- **/
- function popupComfirm(modeName) {
- let conf_str = false;
- conf_str = confirm("小可爱,你即将执行“" + modeName + "”操作!!!" + "\n\n" +
- "根据选择资源数量的不同,会打开相应数量的页面,如果数量较多,请不要惊慌,因为这些页面会自动关闭的哦!!!" + "\n\n" +
- "你是否按照上一个提示,进行了相应的操作?" + "\n\n" + "如果是,你是否要开始执行本次操作?");
- return conf_str;
- }
-
- /**
- * cleaning data 数据清洗 inputString -> idsArr
- */
- function cleanData(inputString) {
- //去除字符串中的所有空格
- inputString.replace(/\s*/g, "");
- //去掉首尾的 - 字符
- if (inputString.charAt(0) == "-") {
- if (inputString.charAt(inputString.length - 1) == "-")
- inputString = inputString.substring(1, inputString.length - 1);
- else
- inputString = inputString.substring(1, inputString.length);
- } else if (inputString.charAt(inputString.length - 1) == "-") {
- inputString = inputString.substring(0, inputString.length - 1);
- }
- //console.log(inputString + "/" + inputString.charAt(0) +"/" + inputString.charAt(inputString.length-1));
- //分割出要点击的栏号,存入数组,用于映射出对应的资源栏id
- let idsArr = inputString.split("-");
- //去重并排序
- idsArr = removeDuplicate(idsArr).sort();
- //去除超出资源栏总数的无效值
- let temp = [];
- for (let i = 0; i < idsArr.length; i++) {
- // console.log("srcBarSum is" + srcBarSum);
- if (idsArr[i] <= srcBarSum && idsArr[i] > 0) {
- temp.push(idsArr[i]);
- // console.log("temp is" + temp);
- }
- }
- // console.log("idsArr is" + idsArr);
- return idsArr = temp;
-
- }
-
- /**
- * 根据模式名执行对应的批量处理操作
- *
- * 点击确认按钮弹出确认弹窗,
- * 如果确认执行,则执行点击操作,
- * 否则执行 取消操作
- */
- function batchForMoreSrcBars(modeName, ids) {
- if (ids.length == 0)
- ids.push(".res-row-box");
-
- let isDownloadMode = modeName == "模拟点击" ? "false" : (modeName == "批量下载" ? "true" : "其他");
-
- if (popupComfirm(modeName)) {
- try {
- // console.log(chosenIDs);
- let startIndex = $("#head").val();
- let endIndex = $("#tail").val();
- for (let id of ids) {
- // console.log(thisID);
- try {
- batch(isDownloadMode, id, startIndex, endIndex);
- } catch (e) {
- console.log(id + "该栏执行异常,跳过执行");
- continue;
- }
- }
- } finally {
- //点击完成,提示刷新页面
- setTimeout(refreshPage, 0);
- //置空栏号输入框
- $(".indexNum").val("");
-
- }
- } else {
- cancel();
- }
- }
-
- /**
- * Click or download in bulk according to
- * isDownload : true -> Download Mode ; false -> Click Mode
- * thisBarID : 此次要执行的资源栏 id
- * startIndex : 此次资源栏中执行的开始资源编号
- * endIndex : 此次资源栏中执行的结束资源编号
- *
- */
- function batch(isDownload, thisBarID, startIndex, endIndex) {
- //let isDownloadMesg = isDownload == "false" ? "模拟点击" : "批量下载";
-
- // 以下五个等价,实现相同功能,但写法是逐步优化
- // var list = document.getElementsByClassName("res-row-open-enable");
- // var list = $(".res-row-open-enable");
- // var list = $(".hide-div").children();
- // var list = $(".res-row-box").children(".hide-div").children();
- let list = $(thisBarID).children(".hide-div").children();
- let succNum = 0;
- let failNum = 0;
- let tempUrl;
- let win;
-
- let actualStartIndex = startIndex <= list.length && startIndex > 0 ? startIndex : (startIndex <= 0 ? 1 : list.length); //小于0则为 1 ; 大于 最大值 则为 最大值
- let actualEndIndex = endIndex <= list.length && endIndex > 0 ? endIndex : (endIndex <= 0 ? 1 : list.length); //输入值超出资源总数的值,则将输入值置为总数的值
- if (actualStartIndex > actualEndIndex) {
- //console.log("here");
- alert("小可爱😀,你的起始结束值写反了哟!");
- return;
- }
- // console.log("actualStartIndex: " + actualStartIndex);
- // console.log("actualEndIndex: " + actualEndIndex);
- // list 存在并不为空
- if (null == list || list.length == 0) {
- console.log(thisBarID + "对应的资源栏为空");
- } else {
-
- for (let i = actualStartIndex - 1; i < actualEndIndex; i++) {
- // console.log(i);
- // console.log(list);
- // console.log(list[i]);
- try {
-
- tempUrl = list[i].getAttribute("data-href");
- if (null == tempUrl || tempUrl == "") {
-
- console.log("资源栏:" + thisBarId + "的第 " + (i + 1) + " 条资源未获取到URL");
-
- } else {
-
- win = window.open(tempUrl);
- if (isDownload == 'false') {
- sleep(100); //睡眠,是为了确保每个资源都被正常获取
- win.close();
- }
- succNum++;
- // console.log(tempUrl);
-
- }
- } catch (e) {
- console.log(e.name + ": " + e.message);
- console.log("资源栏:" + thisBarId + "的第 " + (i + 1) + " 条未成功执行 ;URL : " + list[i].getAttribute("data-href"));
- failNum++;
- continue;
- }
- }
-
- }
- console.log("共检索到 " + list.length + "条; 成功执行 " + succNum + " 次! 失败 " + failNum + " 次! 操作范围:从第 " + actualStartIndex + " 条 至 第 " + actualEndIndex + " 条。");
- }
-
- /**
- * click all resources in two ways according to 'isPositive'
- */
- function clickAll(isPositive) {
-
- let isPositiveMesg = isPositive == "true" ? "正序点击" : "倒序点击";
-
- let conf_str = false;
- conf_str = confirm("小可爱,你即将执行“" + isPositiveMesg +
- "全部资源”操作,如果资源量较大(> 1000),耗时就会较久,打开的页面也会较多哦!不过都会自动关闭的哦!!!" + "\n\n" +
- "小可爱,资源较多时,还请三思啊!!!" + "\n\n" + "你是否要执行?");
- if (conf_str) {
- let list = document.getElementsByClassName("res-row-open-enable");
- let succNum = 0;
- let failNum = 0;
- let tempUrl;
- let win;
- if (isPositive == "true") {
- for (let i = 0; i < list.length; i++) {
- try {
- tempUrl = list[i].getAttribute("data-href");
- win = window.open(tempUrl);
- sleep(100); //睡眠,是为了确保每个资源都被正常获取
- win.close();
- succNum++;
- // console.log(tempUrl);
- } catch (e) {
- console.log(e.name + ": " + e.message);
- console.log("该条未成功执行 ;URL : " + list[i].getAttribute("data-href"));
- failNum++;
- continue;
- }
- }
- } else {
- for (let i = list.length - 1; i >= 0; i--) {
- try {
- tempUrl = list[i].getAttribute("data-href");
- win = window.open(tempUrl);
- sleep(100); //睡眠,是为了确保每个资源都被正常获取
- win.close();
- succNum++;
- // console.log(tempUrl);
- } catch (e) {
- console.log(e.name + ": " + e.message);
- console.log("该条未成功执行 ;URL : " + list[i].getAttribute("data-href"));
- failNum++;
- continue;
- }
- }
- }
- console.log(isPositiveMesg + ": 共检索到 " + list.length + "条; 成功执行 " + succNum + " 次! 失败 " + failNum + " 次!");
- setTimeout(refreshPage, 0);
- } else {
- alert("已取消操作!");
- }
- }
-
- /**
- * open a new tab according the url and execute callback function
- */
- function newTabAlert(forWhat, url, option, callback) {
- if (config.isCRX)
- openInTab(url, option, forWhat);
- else
- openInTab(url, option);
- if (typeof callback === "function") {
- callback();
- }
- }
-
-
-
- /******************************************
- * play videos
- *
- */
-
- let playVideoConfig = {
- isContinuous: false,
- isPlayAll: false,
- isPlayPart: false,
- videoDuration: '',
- };
-
-
-
- /**********************************************
- * Play all videos continuously
- */
-
- let arr = [];
- let count = 0;
- let interval;
- let timeout;
- let intervalTime = 4000; //millisecond
- let isContinuousPaly = false;
- let rate = 1; // <=10
- let weight = 1000 / rate;
- let currentVideoIndex = 0;
- let nextVideoIndex = 0; //当前第几个视频
- let bufferTime = 10000; // millisecond
- let maxRate = 8;
- let log = '';
-
- //将所有视频资源存入数组,以作点击使用
- let a = $("div[data-mime='video']");
- let tempArr = Object.keys(a);
- let tpArr = tempArr.slice(0, tempArr.length - 2);
- tpArr.forEach((key) => {
- //console.log(key, a[key]);
- arr.push(a[key]);
- });
- //console.log(arr.length);
- // for (let a in arr) {
- // console.log(a);
- // }
- //
- playVideoConfig.videoDuration = $('.video-duration');
-
-
- function onContinuousPlayFunc() {
-
- if (playVideoConfig.isContinuous) {
- layer.msg('【无效操作】 : 连播功能已开启');
- return;
- }
-
- playVideoConfig.isContinuous = true;
-
- if (typeof ($("#continuousPlay").attr("class")) != "undefined") {
- let text = "连续播放已开启,无需重复开启";
- //alert(text);
- notification(getNotificationDetails(text), null);
- //layer.msg("test");
- return;
- }
-
- alert("请先关闭 【 Win10 专注助手 】 再使用,否则无法正常提示信息 \n\n 提示:在通知托盘中关闭");
-
-
- // $('<div id = "continuousPlay" class="mejs__button">\
- // <button type="button" aria-controls="mep_0" title="开始连续播放" aria-label="Play" tabindex="0"></button>\
- // </div>\
- // <div id = "stopContinuousPlay" class="mejs__button mejs__playpause-button mejs__pause">\
- // <button type="button" aria-controls="mep_0" title="暂停连续播放" aria-label="Pause" tabindex="0"></button>\
- // </div>\
- // <div id = "continuousPlayN" class="mejs__button">\
- // <button type="button" aria-controls="mep_0" title="开始连续播放(n)" aria-label="Play" tabindex="0"></button>\
- // </div>\
- // <div id = "stopContinuousPlayN" class="mejs__button mejs__playpause-button mejs__pause">\
- // <button type="button" aria-controls="mep_0" title="暂停连续播放(n)" aria-label="Pause" tabindex="0"></button>\
- // </div>\
- // ').insertAfter(".mejs__fullscreen-button");
-
- $('<div id="helper-btn" class="content-center" style="background-color:rgba(255, 255, 255, 0.5);">\
- <span id="continuousPlayAll" class="video-btn content-center"><i class="fa fa-play-circle" aria-hidden="true" style="cursor:pointer"></i></span>\
- <span id="stopContinuousPlayAll" class="video-btn content-center"><i class="fa fa-stop-circle" aria-hidden="true" style="cursor:pointer"></i></span>\
- <span id="continuousPlayPart" class="video-btn content-center"><i class="fa fa-play" aria-hidden="true" style="cursor:pointer"></i></span>\
- <span id="stopContinuousPlayPart" class="video-btn content-center"><i class="fa fa-stop" aria-hidden="true" style="cursor:pointer"></i></span>\
- </div>').insertBefore("#preview-video");
-
- //For all
- $("#continuousPlayAll").click(() => {
- startContinuousPlayAll();
- });
-
- $("#stopContinuousPlayAll").click(() => {
- stopContinuousPlayAll();
-
- });
-
-
- //For part
- $("#continuousPlayPart").click(() => {
- startContinuousPlayForPart();
- });
-
- $("#stopContinuousPlayPart").click(() => {
- stopContinuousPlayForPart();
- });
-
-
-
- let txt = "连续播放已开启,请到视频播放页面使用";
- notification(getNotificationDetails(txt), null);
- }
-
-
- /**
- * close continuous play
- *
- */
- function offContinuousPlayFunc() {
-
- if (!playVideoConfig.isContinuous) {
- layer.msg('【无效操作】 : 连播功能已关闭');
- return;
- }
-
- $('#continuousplayAll, #stopContinusPlayAll, #continuousPlayPart, #stopContinusPlayPart').unbind();
- $('#helper-btn').remove();
- if (playVideoConfig.isPlayAll) {
- stopContinuousPlayAll();
- }
- if (playVideoConfig.isPlayPart) {
- stopContinuousPlayForPart();
- }
- playVideoConfig.isContinuous = false;
- layer.msg('连播功能已关闭!');
- }
-
-
-
- /**
- * For all
- * steps that must be taken when start playing continuously
- */
- function startContinuousPlayAll() {
-
- if (playVideoConfig.isPlayPart) {
- layer.msg('【无效操作】 : 正常连播进行中...');
- return;
- }
- if (isContinuousPaly) {
- layer.msg("【无效操作】:全部连播进行中...");
- return;
- }
-
-
- if (playBySpecifiedRateAndNotify()) {
- $('.video-duration').remove();
- playVideoConfig.isPlayAll = true;
- isContinuousPaly = true;
- layer.msg('禁用进度条', function () {
- clickDiv();
- });
-
- } else {
- notification(getNotificationDetails("已取消本次操作!"), null);
- }
- }
-
- /**
- * For all
- * steps that must be taken when stopping playing continuously
- */
- function stopContinuousPlayAll() {
-
-
- if (playVideoConfig.isPlayPart) {
- stopContinuousPlayForPart();
- playVideoConfig.isPlayPart = false;
- }
-
- if (!isContinuousPaly) {
- layer.msg("【无效操作】:全部连播未执行");
- return;
- }
- // console.log("llllllllll:"+$('.video-duration') );
- if ($('.video-duration').length == 0) {
- $(playVideoConfig.videoDuration).insertAfter('#mep_0');
- }
- isContinuousPaly = false;
- playVideoConfig.isPlayAll = false;
- //停掉当前还未执行完的 interval timeout
- clearInterval(interval);
- clearTimeout(timeout);
- layer.msg('全部连播已关闭');
- let stopContinusPlayText = "已退出连续播放模式,但保留了关闭视频即可看完功能;\n下一次连续播放从第 " + (nextVideoIndex + 1) + " 个视频开始。";
- //alert(stopContinusPlayText);
- notification(getNotificationDetails(stopContinusPlayText), null);
- }
-
- /**
- * For part
- * start playing continuously part of all the specified videos
- */
- function startContinuousPlayForPart() {
-
- if (playVideoConfig.isPlayAll) {
- layer.msg('【无效操作】 : 全部连播进行中...');
- return;
- }
- if (videoConfig.isContinuousPaly) {
- layer.msg("【无效操作】:正常连播进行中...");
- return;
- }
-
-
- playVideoConfig.isPlayPart = true;
- videoConfig.isContinuousPaly = true;
- play(videoConfig.currentVideoDivs);
- }
-
- //部分连播
- //开始时不需要设置播放速度,
- //结束时不需要提示下次播放位置
- //只需开始/结束提即可
- /**
- * For part
- * stop playing continuously part of all the specified videos
- */
- function stopContinuousPlayForPart() {
-
-
- if (playVideoConfig.isPlayAll) {
- stopContinuousPlayAll();
- playVideoConfig.isPlayAll = false;
- }
-
-
- if (!videoConfig.isContinuousPaly) {
- layer.msg("【无效操作】:正常连播未执行");
- return;
- }
-
- videoConfig.isContinuousPaly = false;
- playVideoConfig.isPlayPart = false;
-
- let video = document.querySelector('video');
- let isPaused = video.paused;
- if (isPaused) {
- video.play();
- video.pause();
- } else {
- video.pause();
- video.play();
- }
- }
-
- /**
- * unlock progress bar and click this div
- * return the index of layer
- */
- function unlockBarAndClickDiv(div, func) {
- layer.msg('解锁进度条中...', {
- time: 1500,
- },
- function () {
- let info = '未上锁';
- if ($(div).attr('data-drag') == 'N') {
-
- $(div).attr('data-drag', 'Y');
- info = '已解锁!';
-
- }
- layer.msg(info, {
- time: 1500
- },
- function () {
- $(div).trigger('click');
- if (typeof func === "function") {
- func();
- }
- });
-
- });
- }
-
- /**
- * is or not a number
- * @param {[type]} val [description]
- * @return {Boolean} [description]
- */
- function isNumber(val) {
-
- var regPos = /^\d+(\.\d+)?$/; //非负浮点数
- var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数
- if (regPos.test(val) || regNeg.test(val)) {
- return true;
- } else {
- return false;
- }
-
- }
-
- /**
- * play next videos according to the specified rate inputed by user and notify user
- */
- function playBySpecifiedRateAndNotify() {
-
- let inputRate = prompt("以几倍速度进行连续播放呀🧐(最高" + maxRate + "倍哦!)建议1.8倍最佳🤭");
- //console.log(inputRate);
- if (inputRate == null) {
- return false;
- }
- if (!isNumber(inputRate)) {
- let text = "啥❓ 你输入了啥,那是数字吗?\n 再输一次吧,别输出咯!😀";
- notification(getNotificationDetails(text), null);
- return false;
- }
- rate = inputRate <= 0 ? 1 : (inputRate > maxRate ? maxRate : inputRate);
- weight = 1000 / rate;
- let text = "连续播放已开始!\n将以 " + rate + " 倍速 播放 " + (arr.length - nextVideoIndex) + " 个视频。";
- //console.log(text);
- notification(getNotificationDetails(text), null);
- return true;
-
- }
- /**
- * edit those value about duration before sending ajax
- */
- function send() {
- $.ajaxSetup({
- beforeSend: function () {
- let argsData = arguments[1].data
- let falseArgsData = "";
- let falseVal;
- for (let k in argsData) {
-
- if (k.includes("watch_to")) {
- //console.log("before: " + k + " : " + argsData[k]);
- falseVal = argsData.duration;
- //console.log("after: " + k + " : " + falseVal);
- } else {
- falseVal = argsData[k];
- }
- falseArgsData = falseArgsData + "&" + k + "=" + falseVal;
- }
- arguments[1].data = falseArgsData.substring(1, falseArgsData.length);
- },
- processData: false,
- complete: function () {
- console.log("send completed");
- }
- });
- }
- /**
- * trigger the click action of the current DIV
- *
- */
- function clickDiv() {
- currentVideoIndex = count++;
- nextVideoIndex = currentVideoIndex + 1;
-
- if (isContinuousPaly == false) {
- //console.log("在播放第 " + (nextVideoIndex) + " 个视频时退出了连续播放");
- return;
- }
-
- //第一次使用连续播放开启 关闭即可看完
- if (currentVideoIndex == 0) {
- send();
- }
-
- if (currentVideoIndex < arr.length) {
- // $(arr[currentVideoIndex]).trigger("click");
- unlockBarAndClickDiv(arr[currentVideoIndex], playThisVideo);
- // setTimeout(function() {layer.close(index)}, videoConfig.loadingTime);
- //console.log(currentVideoIndex + " : " + arr[currentVideoIndex]);
- // playThisVideo();
- } else {
- setTimeout(() => {
- clearInterval(interval);
- }, 0);
- //console.log("currentVideoIndex: " + currentVideoIndex);
- //关掉最后一个视频
- $(".close-window").trigger("click");
- alert("连续播放结束, 共连续播放了 " + arr.length + " 个视频,即将刷新页面");
- location.reload();
- }
- }
-
- /**
- * play the current video until it is over and play the next video
- */
- function playThisVideo() {
- if (currentVideoIndex >= arr.length) {
- return;
- }
- let duration;
- let currentTime;
- setTimeout(() => {
- let video = document.querySelector('video');
- let duration = video.duration;
- let currentTime = video.currentTime;
- let isPaused = video.paused;
- //console.log(isPaused ? "暂停" : "未停");
- // if video has paused then play the video
- if (isPaused) {
- $(".mejs__replay").trigger("click");
- //console.log("开始播放");
- }
- setTimeout(() => {
- //console.log(document.querySelector('video').paused ? "暂停" : "未停");
- }, 500);
- video.playbackRate = rate;
-
- //second --> millisecond
- let remain = (duration - currentTime) * weight;
-
- console.log("该视频剩余播放时长 :" + remain + " 毫秒");
-
- //停掉上一个interval timeout
- clearInterval(interval);
- clearTimeout(timeout);
-
- //is NaN
- if (duration != duration || currentTime != currentTime || remain != remain) {
-
- stopContinuousPlayAll();
- notification(getNotificationDetails("执行异常,已停止本次连播。" +
- "\n下一次连续播放从第 " + (nextVideoIndex + 1) + " 个视频开始。", 10000), null);
- return;
-
- }
-
-
- interval = setInterval(clickDiv, remain + intervalTime);
-
- timeout = setTimeout(() => {
- //console.log("当前视频播放到:" + document.querySelector('video').currentTime);
- $(".close-window").trigger("click");
- //console.log("关闭第" + nextVideoIndex + "个视频");
- //console.log(intervalTime + " 毫秒后播放下一个视频");
- }, remain);
-
- }, bufferTime);
- }
-
-
- /**
- * play all the videos since this video
- */
- let videoConfig = {
- videoSum: 0,
- currentVideoId: '',
- currentVideoDivs: arr,
- isContinuousPaly: false,
- loadingTime: 6000,
- rate: 1,
-
- }
-
- $("div[data-mime='video']").each(function (i, e) {
- let ts = $(this);
- ts.attr('id', 'vdoId_' + i);
- ts.bind('click', function (event) {
- /* Act on the event */
- let id = videoConfig.currentVideoId = ts.attr('id');
- let split = id.split('_');
- let newFirstIndex = Number(split[1]);
- videoConfig.currentVideoDivs = arr.slice(newFirstIndex);
- //console.log(videoConfig.currentVideoDivs);
-
- });
- videoConfig.videoSum = ++i;
- });
-
-
- //每拿一个阻塞一次,
- function play(videosArr) {
-
- layer.msg(
- "连播开始!(共" + videosArr.length + "个)", {
- time: 3000
- },
-
- async function () {
- let isOver = true;
- for (let i = 0; isOver && i < videosArr.length; i++) {
- //console.log("time:" + i);
- isOver = await playOne(videosArr[i]);
-
- }
- //console.log("Done all");
- videoConfig.isContinuousPaly = false;
- layer.msg("连播结束!");
- }
- );
- }
-
-
-
- function playOne(div) {
- unlockBarAndClickDiv(div);
- let index = layer.load();
-
- // if(document.querySelector('video').readyState == 4){
- // layer.msg("OK");
- // };
- return new Promise(resolve => {
- setTimeout(() => {
-
- //close load
- layer.close(index);
-
- let video = document.querySelector('video');
-
- let onPause = function () {
- let a = video.currentTime == 0 || video.currentTime == video.duration;
- let b = videoConfig.isContinuousPaly;
- if (b && a) {
- //视频播完会回到开头如果没有回到开头应该在结尾
- resolve(true);
- //console.log("连播&结束");
- } else if (!b && !a) {
- //如果按下暂停前关闭了连续播放 == 结束本次列表循环
- resolve(false);
- //console.log("play stopped");
- } else if (b && !a) {
- //如果还在连续播放但是按下暂停 == 暂停 ,什么也不做
- //console.log("play blocked");
- } else if (!b && a) {
- //不再连播但播放结束
- resolve(false);
- //console.log("不连播&结束");
- }
- }
-
- video.removeEventListener('pause', onPause, false);
- // let duration = video.duration;
- // let currentTime = video.currentTime;
- let isPaused = video.paused;
- //console.log(isPaused ? "本是暂停" : "本是播放");
- // if video has paused then play the video
- if (isPaused) {
- $(".mejs__replay").trigger("click");
- //console.log("暂停-》开始播放");
- }
- setTimeout(() => {
- //console.log(document.querySelector('video').paused ? "依旧暂停" : "已打开播放");
- }, 500);
- video.playbackRate = keyboardEvent.currentSpeed;
- // video.addEventListener("ended", function() {
- // resolve(true);
- // console.log("this over");
- // });
- video.addEventListener('pause', onPause);
-
- }, videoConfig.loadingTime);
-
- });
-
- }
-
-
- /**********************************************
- * keyMap module
- */
- let keyboardEvent = {
- keyBindings: [],
- speedStep: 0,
- rewindTime: 0,
- advanceTime: 0,
- fastSpeed: 0,
- slowerKeyCode: 0,
- fasterKeyCode: 0,
- rewindKeyCode: 0,
- advanceKeyCode: 0,
- resetKeyCode: 0,
- fasterKeyCode: 0,
- currentSpeed: 1.0,
- functionKey: {
- keyMap: 0,
- playAll: 0,
- stopPlayAll: 0,
- playPart: 0,
- stopPlayPart: 0,
- onContinuousPlayFunc: 0,
- offContinuousPlayFunc: 0,
- showTips: 0
-
- },
- keyMapInfo: ``,
- keyMapDetail: []
-
- };
-
-
- // for video
- keyboardEvent.keyBindings.push({
- action: "slower",
- key: Number(keyboardEvent.slowerKeyCode) || 83,
- value: Number(keyboardEvent.speedStep) || 0.1,
- force: false,
- predefined: true
- }); // default S
- keyboardEvent.keyBindings.push({
- action: "faster",
- key: Number(keyboardEvent.fasterKeyCode) || 87,
- value: Number(keyboardEvent.speedStep) || 0.1,
- force: false,
- predefined: true
- }); // default: W
- keyboardEvent.keyBindings.push({
- action: "rewind",
- key: Number(keyboardEvent.rewindKeyCode) || 65,
- value: Number(keyboardEvent.rewindTime) || 10,
- force: false,
- predefined: true
- }); // default: A
- keyboardEvent.keyBindings.push({
- action: "advance",
- key: Number(keyboardEvent.advanceKeyCode) || 68,
- value: Number(keyboardEvent.advanceTime) || 10,
- force: false,
- predefined: true
- }); // default: D
- keyboardEvent.keyBindings.push({
- action: "reset",
- key: Number(keyboardEvent.resetKeyCode) || 82,
- value: 1.0,
- force: false,
- predefined: true
- }); // default: R
- keyboardEvent.keyBindings.push({
- action: "fast",
- key: Number(keyboardEvent.fastKeyCode) || 71,
- value: Number(keyboardEvent.fastSpeed) || 1.8,
- force: false,
- predefined: true
- }); // default: G
-
-
- // for functions
- keyboardEvent.keyBindings.push({
- action: 'keyMap',
- key: Number(keyboardEvent.functionKey.keyMap) || 77
- }); // M
- keyboardEvent.keyBindings.push({
- action: 'playAll',
- key: Number(keyboardEvent.functionKey.playAll) || 90
- }); // Z
- keyboardEvent.keyBindings.push({
- action: 'stopPlayAll',
- key: Number(keyboardEvent.functionKey.stopPlayAll) || 88
- }); // X
- keyboardEvent.keyBindings.push({
- action: 'playPart',
- key: Number(keyboardEvent.functionKey.playPart) || 67
- }); // C
- keyboardEvent.keyBindings.push({
- action: 'stopPlayPart',
- key: Number(keyboardEvent.functionKey.stopPlayPart) || 86
- }); // V
- keyboardEvent.keyBindings.push({
- action: 'onContinuousPlayFunc',
- key: Number(keyboardEvent.functionKey.onContinuousPlayFunc) || 66
- }); // B
- keyboardEvent.keyBindings.push({
- action: 'offContinuousPlayFunc',
- key: Number(keyboardEvent.functionKey.offContinuousPlayFunc) || 78
- }); // N
-
-
- keyboardEvent.keyBindings.push({
- action: 'showTips',
- key: Number(keyboardEvent.functionKey.showTips) || 84
- }); // T
-
-
- /**
- * get the content of the action specified
- * the action bound to some event
- * @return json
- */
- function getKeyBindingsByAction(action) {
-
- let item = keyboardEvent.keyBindings.find(item => item.action === action);
- return item;
-
- }
-
- /**
- * get the value by specified action and keyname
- * @param {string} action [the action bound to some event]
- * @param {string} keyname
- * @return {[type]}
- */
- function getValueByActionAndKeyname(action, keyname) {
- return getKeyBindingsByAction(action)[keyname];
- }
-
- /**
- * [get all values by specified keyname ]
- * @return {[array]} [all values]
- */
- function getAllValuesByKeyname(keyname) {
- let all = [];
- let arr = keyboardEvent.keyBindings;
- for (let i in arr) {
- let x = arr[i];
- /**
- * access value by variable key
- * x.keyname ==> x[keyname]
- */
- all.push(x[keyname]);
- }
- // console.log('all:'+ all);
- return all;
- }
-
- function changeKeycode(keycodeArr, toLowercase) {
-
- let arr = [];
- for (let i in keycodeArr) {
- // if (toLowercase) {
- // arr.push(keycodeArr[i] + 32);
- // }else{
- // arr.push(keycodeArr[i] - 32);
- // }
- toLowercase == true ? arr.push(keycodeArr[i] + 32) : arr.push(keycodeArr[i] - 32);
-
- }
- //console.log(keycodeArr + '****' + arr);
-
- return arr;
-
- }
-
- /**
- * initialize keyboardEvent: keyMapInfo keyMapDetail
- * @type {[type]}
- */
-
- keyboardEvent.keyMapDetail = [
-
- ['强制关闭Chrome', 'Alt + F4'],
- ['查看快捷键', 'shift + m'],
- ['弹出提示', 'shift + t'],
- [`视频加速 (+${getKeyBindingsByAction('faster').value})`, 'W'],
- [`视频减速 (-${getKeyBindingsByAction('slower').value})`, 'S'],
- [`视频快退 ${getKeyBindingsByAction('rewind').value}s`, 'A'],
- [`视频快进 ${getKeyBindingsByAction('advance').value}s`, 'D'],
- [`最佳倍速 (${getKeyBindingsByAction('fast').value})`, 'G'],
- [`重置倍速 (${getKeyBindingsByAction('reset').value})`, 'R'],
- ['开启连播', 'shift + b'],
- ['关闭连播', 'shift + n'],
- ['开始正常连播', 'shift + c'],
- ['结束正常连播', 'shift + v'],
- ['开始全部连播', 'shift + z'],
- ['结束全部连播', 'shift + x']
-
- ];
- //获取 快捷键列表
- function getKeyMapView() {
- let viewArr = keyboardEvent.keyMapDetail.map((item) => {
- return `<p class="content-center"><span class="keyMap-name"> ${item[0]} </span> <span class="keyMap-value"> ${item[1]} </span></p>`
- });
- return viewArr.join(' ');
- }
-
- keyboardEvent.keyMapInfo = `
- <div id="keyMapInfo">
- <p class="content-center keyMap-head"><span class="keyMap-name">功能</span><span class="keyMap-value">快捷键</span></p>
- <hr>
- ${getKeyMapView()}
- </div>
- `;
-
- /**
- * bind keyboard eventListener to document
- */
- let lastTimeStamp = 0;
- let isSameKey = false;
- let lastKeyCode = 0;
- let recent2KeysInterval = 0;
- $(document).bind('keypress', function (event) {
- /* 禁止频繁操作 */
- let curTimeStamp = event.timeStamp;
- recent2KeysInterval = curTimeStamp - lastTimeStamp;
- lastTimeStamp = curTimeStamp;
- if (recent2KeysInterval < 200) {
- layer.msg("操作过于频繁");
- return;
- }
-
- /* Act on the event */
- let keyCode = event.keyCode;
- let altKey = event.altKey;
- let ctrlKey = event.ctrlKey;
- let shiftKey = event.shiftKey;
- //console.log("keyCode:" + keyCode);
-
- /* 记录最近两次按下是否为同一个 key */
- isSameKey = lastKeyCode == keyCode ? true : false;
- lastKeyCode = keyCode;
-
-
- let lowercase = changeKeycode(getAllValuesByKeyname('key').slice(0, 6), true);
- // console.log('[119, 115, 97, 100, 114, 103]:' + lowercase);
- let funcKeyLowercase = changeKeycode(getAllValuesByKeyname('key').slice(6), true);
- // console.log("[109, 122, 120, 99, 118, 98, 110]:" + funcKeyLowercase);
- let funcKeyUppercase = getAllValuesByKeyname('key').slice(6);
- // console.log("[77, 90, 88, 67, 86, 66, 78]:" + funcKeyUppercase);
- let playVdoFuncKeyLowercase = getAllValuesByKeyname('key').slice(7, 11);
-
- // shift + lowercase => uppercase 小写键盘
- let shiftAndLowercase = shiftKey && ((funcKeyUppercase.find(item => item === keyCode) === undefined ? false : true));
- // shift + uppercase => lowercase 大写键盘
- let shiftAndUppercase = shiftKey && ((funcKeyLowercase.find(item => item === keyCode) === undefined ? false : true));
-
- let shiftAndPlayVdoLowercase = shiftKey && ((playVdoFuncKeyLowercase.find(item => item === keyCode) === undefined ? false : true));
-
- if (!document.querySelector('video').paused) {
-
- if (lowercase.find(item => item === keyCode)) {
- //console.log("is pause:"+ document.querySelector('video').paused);
- layer.msg('请打开大写键盘 以使用 【视频控件】');
- return;
- }
- if (shiftAndUppercase) {
- layer.msg('请关闭大写键盘 以使用完整的快捷键功能');
- return;
- }
- if (shiftAndPlayVdoLowercase) {
- if (!playVideoConfig.isContinuous) {
- layer.msg('请先开启连播功能');
- return;
- }
-
- }
-
- } else if (document.querySelector('video').paused) {
-
- if (shiftAndUppercase) {
- layer.msg('请关闭大写键盘!以使用完整的快捷键功能');
- return;
- }
- //四个连播功能(ZXCV)在没有开启连播时,提醒开启连播功能
- if (shiftAndPlayVdoLowercase) {
- if (!playVideoConfig.isContinuous) {
- layer.msg('请先开启连播功能');
- return;
- }
- }
- if (!shiftAndLowercase) {
- return;
- }
-
- }
-
-
- let item = keyboardEvent.keyBindings.find(item => item.key === keyCode);
- if (item) {
-
- let video = document.querySelector('video');
- doAction(item, video);
-
- }
-
- });
-
-
-
- /**
- * [doAction description]
- * @param {[type]} item [that event triggered]
- * @param {[type]} video [description]
- */
- function doAction(item, video) {
-
- let action = item.action;
- let value = item.value;
- let num = (video.playbackRate).toFixed(1);
-
- /**
- * send a record ( special Keys )
- */
- if (keyboardEventMap.has(action)) {
-
- if (
- !(specialKeyboardEventMap.has(action)
- && isSameKey
- && recent2KeysInterval < statConfig.specialKeysInterval)
- ) {
- // console.log('not special keys')
- // console.log(specialKeyboardEventMap.has(action));
- // console.log(isSameKey);
- // console.log(recent2KeysInterval);
- record(keyboardEventMap.get(action))
- }
- }
-
-
- if (action == 'slower') {
-
- video.playbackRate -= value;
- num = (video.playbackRate).toFixed(1);
- keyboardEvent.currentSpeed = num;
- layer.msg(num + " 倍");
-
- } else if (action == 'faster') {
-
- video.playbackRate += value;
- num = (video.playbackRate).toFixed(1);
- keyboardEvent.currentSpeed = num;
- layer.msg(num + " 倍");
-
- } else if (action == 'rewind') {
-
- video.currentTime -= value;
- layer.msg("- " + value + 's');
- return;
-
- } else if (action == 'advance') {
-
- video.currentTime += value;
- layer.msg("+ " + value + 's');
- return;
-
- } else if (action == 'reset') {
-
- video.playbackRate = value;
- num = (video.playbackRate).toFixed(1);
- keyboardEvent.currentSpeed = num;
- layer.msg(num + " 倍");
-
- } else if (action == 'fast') {
-
- video.playbackRate = value;
- num = (video.playbackRate).toFixed(1);
- keyboardEvent.currentSpeed = num;
- layer.msg(num + " 倍");
-
- } else if (action == 'keyMap') {
-
- let i = layer.alert(
- keyboardEvent.keyMapInfo, {
- //icon: 1
- anim: 2
- },
- function (index) {
- //layer.msg('操作成功!');
- layer.close(index);
- });
-
- layer.title('Key Map', i);
- return;
-
-
- } else if (action == 'playAll') {
- $("#continuousPlayAll").trigger('click');
- return;
-
- } else if (action == 'stopPlayAll') {
- $("#stopContinuousPlayAll").trigger('click');
- return;
-
- } else if (action == 'playPart') {
- $("#continuousPlayPart").trigger('click');
- return;
-
- } else if (action == 'stopPlayPart') {
- $("#stopContinuousPlayPart").trigger('click');
- return;
- } else if (action == 'onContinuousPlayFunc') {
- onContinuousPlayFunc();
- return;
-
- } else if (action == 'offContinuousPlayFunc') {
- offContinuousPlayFunc();
- return;
- } else if (action == 'showTips') {
- showTips();
- return;
- }
-
-
-
- }
-
-
- /************************************
- * tips module
- */
-
- let tipsConfig = {
- params: {
- tipsMore: true,
- tips: 1,
- time: 6000
- },
- };
-
- function showTips() {
- layer.tips('全部连播', '#continuousPlayAll', tipsConfig.params);
- layer.tips('终止全部连播', '#stopContinuousPlayAll', tipsConfig.params);
- layer.tips('正常连播', '#continuousPlayPart', tipsConfig.params);
- layer.tips('终止正常连播', '#stopContinuousPlayPart', tipsConfig.params);
- }
-
- /**********************************
- * statistics
- */
- var meta = '<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"/>';
- $("head").prepend(meta);
- const clickEventMap = new Map([
- ['mode-click', 1],
- ['mode-download', 2],
- ['refresh', 3],
- ['reset', 4],
- ['choose', 5],
- ['confirm', 7],
- ['downloadSrc', 8],
- ['download-res', 9],
- ['forward', 10],
- ['reverse', 11],
- ['continuousPlayAll', 26],
- ['stopContinuousPlayAll', 27],
- ['continuousPlayPart', 28],
- ['stopContinuousPlayPart', 29],
- ]);
- const keyboardEventMap = new Map([
- ['keyMap', 12],
- ['showTips', 13],
- ['faster', 14],
- ['slower', 15],
- ['rewind', 16],
- ['advance', 17],
- ['fast', 18],
- ['reset', 19],
- ['onContinuousPlayFunc', 20],
- ['offContinuousPlayFunc', 21],
- ['playPart', 22],
- ['stopPlayPart', 23],
- ['playAll', 24],
- ['stopPlayAll', 25]
- ]);
- // ?s 内的操作记为 1 次 有效记录
- const specialKeyboardEventMap = new Map([
- ['faster', 14],
- ['slower', 15],
- ['rewind', 16],
- ['advance', 17],
- ]);
- let statConfig = {
- recordURL: config.base + '/hits/saveOrUpdateUsePostWithoutCORS',
- //? s 内记 1
- specialKeysInterval: 5000
- }
- let record = (fcId) => {
-
- let params = {
- htFcId: fcId
- }
- axios({
- method: 'POST',
- url: statConfig.recordURL,
- data: qs.stringify(params),
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded'
- }
- }).then((response) => {
- //succ
- console.log("succ")
- }).catch((error) => {
- //err
- console.log("err")
- });
- }
-
- window.addEventListener("click", (event) => {
- let id = event.target.id;
- if (clickEventMap.has(id)) {
- record(clickEventMap.get(id))
- }
- });
-
-
-
-
- /**
- * MosoteachHelper CSS
- */
- const styleTag = `
- <style>
- .helper-btn{
- border:1px solid #aaa;
- border-radius:25px;
- width:10%;
- color:#fff;
- font-weight:1000;
- box-shadow:darkgrey 3px 3px 7px 2px;
- cursor:pointer;
- transition: .2s;
- }
- .helper-btn-a:hover{
- // opacity: 0.6; //透明度
- // background-color: #4d79ff !important;
- background-color: rgba(0, 151, 179,1) !important;
- box-shadow: darkgrey 2px 2px 5px 1px !important;
- }
- .helper-btn-b:hover{
- background-color:rgba(204, 0, 0,1) !important;
- }
- .helper-btn:active{
- background-color:#002b80 !important;
- border:3px solid #eee !important;
- box-shadow: darkgrey 1px 1px 2px 1px !important;
- }
- #refresh{
- float:right;
- background-color:rgba(204, 0, 0,0.6);
- }
- #reset{
- float:right;
- background-color:rgba(204, 0, 0,0.6);
- }
- #mode-click{ background:rgba(0, 151, 179,0.7);}
- #mode-download{ background:rgba(0, 151, 179,0.7);}
- #confirm{ background:rgba(0, 151, 179,0.7);}
- #downloadSrc{ background:rgba(0, 151, 179,0.7);}
- #choose{ background:rgba(204, 0, 0,0.6);}
- //#refresh{ background:rgba(0, 151, 179,0.7);}
-
- .content-center{
- display: -webkit-box;
- display: -moz-box;
- display: -ms-flexbox;
- display: -webkit-flex;
- display: flex;
- /*垂直居中*/
- -webkit-box-align: center;
- -moz-box-align: center;
- -ms-flex-align: center;
- -webkit-align-items: center;
- justify-content: center;
- align-items: center;
- justify-content: center;
- }
-
- .video-btn{
- color:white;
- font-size:20px;
- width:20%;
- height: 34px;
- }
-
- #keyMapInfo{
- width:300px;
- }
-
- .keyMap-head{
- font-size: 16px;
- font-weight: 700;
- }
-
- .keyMap-name{
- width: 50%;
- font-size: 14px;
- font-weight: 600;
- cursor: pointer;
- }
-
- .keyMap-value{
- width: 50%;
- font-size: 16px;
- font-weight: 700;
- color: orange;
- }
-
- </style>`;
- $(styleTag).appendTo('head');
-
- //为每个资源添加下载按钮
- $(".res-row-open-enable").each(function () {
- if ($(this).find(".download-res-button").length > 0) return; //如果已经存在下载按钮(例如mp3),则不再添加
- $(this).find("ul").html('<li id="download-res" class="download-ress download-res-button">下载</li>' + $(this).find("ul").html());
- // $(this).find("ul").html('<li id="forward">正序点击</li>' + $(this).find("ul").html());
- // $(this).find("ul").html('<li id="reverse">倒序点击</li>' + $(this).find("ul").html());
- });
- //单个资源下载
- $(document).on('click', '#download-res', function () {
- var resHref = $(this).parents(".res-row-open-enable").attr('data-href');
- window.open(resHref);
- });
-
- $('<div id="functionAreaTitle" style="padding:0 20px">\
- <div class="clear20"></div>\
- <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="100%" color=#0BD SIZE=4>\
- <div class="clear10"></div>\
- <div class="res-row-title">\
- <span style="color: #0BD;font-weight:600; font-size:16px"> 功能区 </span>\
- <span > Powered by </span>\
- <span ><a href="https://greasyfork.org/zh-CN/scripts/390978-%E4%BA%91%E7%8F%AD%E8%AF%BE%E9%AB%98%E6%95%88%E5%8A%A9%E6%89%8B">云班课高效助手 </a></span>\
- <span style="color: orange;font-weight:500; font-size:14px"> 查看快捷键 :shift + m </span>\
- <i class="slidedown-button manual-order-hide-part icon-angle-down" data-sort="1001"></i>\
- </div>\
- </div>\
- <div class="clear20"></div>\
- <!-- helper area Start -->\
- <div id="functionAreaContent" class="hide-div" data-status="N" data-sort="1001" style="display: none;">\
- <div id="helper" style="padding:0 40px;">\
- <div class="res-row-title" >\
- <span class="res-group-name">当前模式: </span>\
- <span id="modeName" style="color: #0BD;font-weight:600">未选择 </span>\
- <span class="span-display" style="color: red"> | ( 选择模式后,请按照提示操作,否则会出错;“模拟点击/下载”执行完毕后需刷新页面,数据才会更新。)</span>\
- <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="997"></i>\
- </div>\
- <div class="hide-div" data-status="N" data-sort="997" style="display: none;">\
- <form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
- <input id="mode-click" class="helper-btn helper-btn-a" type="button" value="模拟点击">\
- <input id="mode-download" class="helper-btn helper-btn-a" type="button" value="批量下载">\
- <input id="reset" class="helper-btn helper-btn-a helper-btn-b" type="button" value="重置">\
- <input id="refresh" class="helper-btn helper-btn-a helper-btn-b" type="button" value="刷新页面">\
- </form>\
- </div>\
- <div id="module-3">\
- <div class="clear30"></div>\
- <div class="res-row-title" >\
- <span class="res-group-name" >已选栏号:</span>\
- <span id="barID" style="color: #0BD;font-weight:600"> 全选 </span>\
- <span class="span-display" style="color: #0BD" > | (范围: 最大值为资源栏总数 / 不填写 则视为全选)</span>\
- <span class="span-display" style="color: red">(注意:资源栏号是从资源区里第一栏开始)</span>\
- <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="1000"></i>\
- </div>\
- <div class="hide-div" data-status="N" data-sort="1000" style="display: none;">\
- <form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
- <input id="bar_index" placeholder="选择栏号 [ if (value < 1) --> 1 ; if (value > max) --> max ] 选择多栏语法: 3-2-4-6 " \
- onkeyup="this.value=this.value.replace(/[^\\d][-]/g,\'\')" onafterpaste="this.value=this.value.replace(/[^\\d][-]/g,\'\')" style="border:1px solid #0BD; border-radius:8px;width:86%"> \
- <input id="choose" class="helper-btn helper-btn-a helper-btn-b" type="button" value="重置">\
- </form>\
- </div>\
- </div>\
- <div id="module-1">\
- <div class="clear30"></div>\
- <div class="res-row-title" >\
- <span class="res-group-name" >模拟批量点击/下载</span>\
- <span class="span-display" style="color: #0BD" >(范围:以资源总数值作为范围最大值)</span>\
- <span class="span-display" style="color: red">( 点击对应按钮,将打开较多页面,请耐心等待其自动关闭。可在“控制台”里查看运行日志)</span>\
- <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="998"></i>\
- </div>\
- <div class="hide-div" data-status="N" data-sort="998" style="display: none;">\
- <form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
- <input id="head" class="indexNum" placeholder="起始位置 [ if (value < 1) --> 1 ; if (value > max) --> max ]" style="border:1px solid #0BD; border-radius:8px;width:42%" > \
- <input id="tail" class="indexNum" placeholder="结束位置 [ if (value < 1) --> 1 ; if (value > max) --> max ]" style="border:1px solid #0BD; border-radius:8px;width:42%"> \
- <input id="confirm" class="helper-btn helper-btn-a" type="button" value="模拟点击">\
- <input id="downloadSrc" class="helper-btn helper-btn-a" type="button" value="批量下载">\
- </form>\
- </div>\
- </div>\
- <div id="module-2">\
- <div class="clear30"></div>\
- <div class="res-row-title" >\
- <span class="res-group-name" >模拟全部点击(耗时较长)</span>\
- <span class="span-display" style="color: #0BD" >(范围:所有资源)</span>\
- <span class="span-display" style="color: red">( 点击后,将会自动打开较多页面,请耐心等待其自动关闭。可在“控制台(F12 -> console)”里查看运行日志)</span>\
- <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="999"></i>\
- </div>\
- <div class="hide-div" data-status="N" data-sort="999" style="display: none;">\
- <div class="res-row drag-res-row" style="height:37px !important">\
- <div class="operation manual-order-hide-part" style="float:left;!important">\
- <ul style="margin-top:0px;"><li id="reverse">倒序点击</li><li id="forward">正序点击</li>\
- <div class="clear"></div>\
- </ul>\
- </div>\
- </div>\
- </div>\
- </div>\
- <div id="module-4">\
- <div class="clear30"></div>\
- <div class="res-row-title" >\
- <span class="res-group-name" >功能测试模块 </span>\
- <span style="color: red"><a href = "https://greasyfork.org/en/scripts/390978-%E4%BA%91%E7%8F%AD%E8%AF%BE%E9%AB%98%E6%95%88%E5%8A%A9%E6%89%8B/feedback"> 点此反馈 (维护不易,还请好评 🙇 )</a></span>\
- <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="1002"></i>\
- </div>\
- <div class="hide-div" data-status="N" data-sort="1002" style="display: none;">\
- <div class="res-row drag-res-row" style="height:37px !important">\
- <div class="operation manual-order-hide-part" style="float:left;!important">\
- <ul style="margin-top:0px;">\
- <li id ="continuousPlayMode">视频连续播放控件(按钮在视频界面)</li>\
- <li > 快捷键系统( shift + m )</li>\
- <div class="clear"></div>\
- </ul>\
- </div>\
- </div>\
- </div>\
- </div>\
- </div>\
- </div>\
- <!-- helper area End -->\
- <div id="sourceTitle" style="padding:0 20px">\
- <div class="clear10"></div>\
- <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="100%" color=#0BD SIZE=4>\
- <div class="clear10"></div>\
- <div class="res-row-title">\
- <span style="color: #0BD;font-weight:600; font-size:16px"> 资源区 </span>\
- </div>\
- </div>\
- ').insertAfter("#res-view-way");
- // 初始化
- $("#module-1,#module-2").css("display", "none");
- $("#confirm, #downloadSrc, #mode-click, #mode-download").css("display", "inline");
- // change mode
- $(document).on('click', '#mode-click', function () {
- $("#module-1, #module-2").css("display", "block");
- // 等价于
- // document.getElementById("module-1").style.display="block";
- // document.getElementById("module-2").style.display="block";
- // document.getElementById('confirm').style.display = document.getElementById('confirm').style.display=="inline"?"inline":"none";
- $("#downloadSrc, #mode-download").css("display", "none");
- // $("#mode-click").css({"background-color":"#0BD","color":"#fff"});
- $("#modeName").text("模拟点击");
- if (browserType() == "Chrome") {
- newTabAlert("onDownload", "chrome://settings/downloads", 'active', function () {
- alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
- "(识别到您使用的是Chrome浏览器)" + "\n\n" +
- " 已自动为你打开浏览器【设置】页面" + "\n" +
- " 【提醒】:如果没有结果可在搜索框中搜索【保存位置】" + "\n" +
- " 【 打开 】 “下载前询问每个文件的保存位置” 右侧按钮");
- });
- } else {
- alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
- "(以下只是 chrome 浏览器操作步骤)" + "\n" +
- " 1. 新建 Tab 页\n" + " -->\n" +
- " 2. 地址栏输入: chrome://settings/?search=downloads\n" + " -->\n" +
- " 3. 打开 “下载前询问每个文件的保存位置” 右侧按钮");
- }
- });
- $(document).on('click', '#mode-download', function () {
- document.getElementById("module-1").style.display = "block";
- $("#module-2, #confirm, #mode-click").css("display", "none");
- // $("#mode-download").css({"background-color":"#0BD","color":"#fff"});
- $("#modeName").text("批量下载");
- if (browserType() == "Chrome") {
- newTabAlert("offDownload", "chrome://settings/downloads", 'active', function () {
- alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
- "(识别到您使用的是Chrome浏览器)" + "\n\n" +
- " 已自动为你打开浏览器【设置】页面" + "\n" +
- " 【提醒】:如果没有结果可在搜索框中搜索【保存位置】" + "\n" +
- " 【 关闭 】 “下载前询问每个文件的保存位置” 右侧按钮")
- });
- } else {
- alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
- "(以下只是 chrome 浏览器操作步骤)" + "\n" +
- " 1. 新建 Tab 页\n" + " -->\n" +
- " 2. 地址栏输入:chrome://settings/?search=downloads\n" + " -->\n" +
- " 3. 关闭 “下载前询问每个文件的保存位置” 右侧按钮");
- }
- });
- $(document).on('click', '#reset', function () {
- $("#module-1,#module-2").css("display", "none");
- $("#confirm, #downloadSrc, #mode-click, #mode-download").css("display", "inline");
- // $("#mode-download, #mode-click").css({"background-color":"#fff","color":"#000"});
- $("#modeName").text("未选择");
-
- });
- // 刷新
- $(document).on('click', '#refresh', function () {
- location.reload()
- })
- //资源栏总数
- var srcBarSum = 0;
- // 给分栏添加 id 易于按栏操作
- $(".res-row-box").each(function (i, e) {
- $(this).attr('id', 'id_' + i);
- srcBarSum = i + 1;
- });
- //存储所有被选择的资源栏 id
- var chosenIDs = [];
- $(document).on('click', '#choose', function () {
- //获取点击时按钮值
- var val = $("#choose").val();
- //接受用户输入的id 字符串
- let inputString = $("#bar_index").val().trim();
- //inputString经过清洗后得到的有效资源栏编号
- let idsArr = cleanData(inputString);
-
- if (val == "确认选择") {
- /**
- * 用户修改要选择的资源栏点击确认后
- * 根据有效资源栏编号生成对应资源栏id存入数组备用
- * 并显示被选择的所有有效资源栏
- */
-
- //无输入内容,选择全部栏
- if (idsArr.length == 0) {
- chosenIDs.push(".res-row-box");
- } else {
- //有输入内容,转化成对应的id,放入数组备用
- for (let id of idsArr) {
- chosenIDs.push("#id_" + (id - 1));
- }
- }
- //test
- // console.log(idsArr);
- //var barID = $("#bar_index").val();
- let barID_str = idsArr.length == 0 ? "全选" : idsArr;
- //var barID_str = (barID > 0 && barID < 21) ? barID : "全选";
- alert("小可爱,你已将要操作的资源栏修改为: " + barID_str);
- $("#barID").text(barID_str);
- $("#choose").val("重置");
- $("#choose").css('background-color', 'rgba(204, 0, 0,0.6)');
-
- } else {
- /**
- * 用户重置资源栏输入框
- * 置空输入框 和 存储的所有资源栏id
- * 被选择的资源栏设为全选
- */
- $("#bar_index").val("");
- chosenIDs = [];
- $("#choose").val("确认选择");
- $("#choose").click();
-
- }
-
- });
-
- // reset bar_index
- $('#bar_index').bind("input propertychange", function (event) {
- $("#choose").val("确认选择");
- $("#choose").css('background-color', 'rgba(0, 151, 179,0.7)');
- });
-
-
- /**
- * Main body
- *
- */
-
- /**
- * 根据指定的所有资源栏id,进行模拟点击
- */
- $(document).on('click', '#confirm', function () {
- batchForMoreSrcBars("模拟点击", chosenIDs)
- });
-
- /**
- * 根据指定的所有资源栏id,进行批量下载
- *
- */
- $(document).on('click', '#downloadSrc', function () {
- batchForMoreSrcBars("批量下载", chosenIDs)
- });
-
- /**
- * 模拟正序点击全部资源
- *
- */
- $(document).on('click', '#forward', function () {
- clickAll("true")
- });
-
- /**
- * 模拟倒序点击全部资源
- *
- */
- $(document).on('click', '#reverse', function () {
- clickAll("false")
- });
-
- /**
- * Play videos continuously
- */
- $(document).on('click', '#continuousPlayMode', () => {
- continuousPlay()
- })
-
-
- });