云班课高效助手

【高效再升级😃!高效使用云班课,一个脚本就够了!😎】 【🧡视频倍速:新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【💛视频连播:新版视频连播功能,支持从当前视频开始连播(配合视频控件,体验更佳)】、【💙快捷键:新增快捷键系统,常用功能都已加入,高效更进一步】、【💚资源处理:批量点击、下载、批处理】

目前為 2020-09-18 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name 云班课高效助手
  3. // @name:zh-CN 云班课高效助手
  4. // @author bellamy.n.h
  5. // @namespace http://tampermonkey.net/
  6. // @version 1.86
  7. // @description 【高效再升级😃!高效使用云班课,一个脚本就够了!😎】 【🧡视频倍速:新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【💛视频连播:新版视频连播功能,支持从当前视频开始连播(配合视频控件,体验更佳)】、【💙快捷键:新增快捷键系统,常用功能都已加入,高效更进一步】、【💚资源处理:批量点击、下载、批处理】
  8. // @match https://www.mosoteach.cn/web/index.php*
  9. // @include *://www.mosoteach.cn/web/index.php*
  10. // @note Version 1.85 —— 1.86 修复连播视频时数量错误BUG;重构快捷键视图生成代码,降冗余;Add Statistical Analysis System;限制对快捷键的频繁操作;特殊处理部分高频使用的快捷键。
  11. // @note Version 1.80 😁【新增视频倍速控件(支持 倍速递加、递减;倍速重置;一键最佳倍速;视频快进、快退)】、【新版视频连播功能,支持从当前视频开始连播(配合视频控件,可达到极度自由)】、【新增快捷键系统,常用功能已都加入,高效更进一步】、【修复模拟点击/下载失效Bug】、【限制全部连播最大速度为8倍】
  12. // @note Version 1.70 视频最高16倍速连播;调用系统通知,反馈更佳;
  13. // @note Version 1.65 偷偷改了些小Bug 🤭,使连播更顺畅。下个版本上16倍速连播喽😊
  14. // @note Version 1.60 新增测试功能,支持 连续播放所有视频、 立即看完当前视频(测试阶段,还请反馈)
  15. // @note Version 1.50 加强对输入值约束; 支持多栏处理; chrome浏览器自动打开 设置页面地址更改; 其他Bug修复。
  16. // @note Version 1.40 优化代码; 新增浏览器类型判断,支持chrome浏览器自动打开 设置页面。
  17. // @note Version 1.32 优化操作反馈 (可以重置已选择的资源栏数)
  18. // @note Version 1.31 修复可能存在的Bug (页面无法自动关闭)
  19. // @icon https://s1.ax1x.com/2020/05/18/Yf6Kcd.png
  20. // @require https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
  21. // @require https://cdnjs.cloudflare.com/ajax/libs/layer/2.3/layer.js
  22. // @require https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
  23. // @require https://cdn.jsdelivr.net/npm/qs@6.9.4/dist/qs.min.js
  24. // @grant GM_openInTab
  25. // @grant GM_notification
  26. // @grant GM_getValue
  27. // @grant GM_setValue
  28. // @grant GM_deleteValue
  29. // @grant GM_listValues
  30. // ==/UserScript==
  31.  
  32.  
  33. $(function () {
  34. 'use strict';
  35.  
  36. var config = {
  37. isCRX: false,
  38. notificationTitle: "云班课高效助手",
  39. icon128: "https://s1.ax1x.com/2020/05/18/Yf6pp4.png",
  40. icon48: "https://s1.ax1x.com/2020/05/18/Yf6Kcd.png",
  41. icon32: "https://s1.ax1x.com/2020/05/18/Yf6BBq.png",
  42. icon16: 'https://s1.ax1x.com/2020/05/18/Yfg71e.png',
  43. layer_css: "https://cdn.jsdelivr.net/npm/layui-layer@1.0.9/layer.min.css",
  44. layer_js: "https://cdnjs.cloudflare.com/ajax/libs/layer/2.3/layer.js",
  45. jquery_js: "https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js",
  46. layui_js: "https://cdn.jsdelivr.net/npm/layui-src@2.5.5/dist/layui.min.js",
  47. fontawesome_css: "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css",
  48. base: "https://mst.bellamy.top:8443",
  49.  
  50. };
  51.  
  52. var openInTab;
  53. var setVal;
  54. var getVal;
  55. var notification;
  56. var delVal;
  57. var listVals;
  58. var qs = Qs;
  59.  
  60. if (config.isCRX) {
  61. console.log("in CRX");
  62. /**
  63. * ***********
  64. * For CRX Begin
  65. * ***********
  66. *
  67. * ***********
  68. * Override the following apis provided by TamperMonkey
  69. * these apis can only work well in TamperMonkey Script
  70. * but they can not work in CRX
  71. */
  72.  
  73. /**
  74. * [GM_openInTab : send message to bg.js to create New Tab according to these following parameters]
  75. * @param {[String]} forWhat [onDownload or offDownload]
  76. * @param {[String]} _url [new tab]
  77. * @param {[Boolean]} _option [is or not active]
  78. */
  79. function GM_openInTab(_url, _option, forWhat) {
  80.  
  81. chrome.runtime.sendMessage({
  82. createTab: forWhat,
  83. url: _url,
  84. option: "active" === _option,
  85. });
  86.  
  87. }
  88.  
  89. function GM_setValue(name, value) {
  90.  
  91. }
  92.  
  93. function GM_getValue(name, defaultValue) {
  94.  
  95. }
  96. /**
  97. * send message to chrome API
  98. * chrome.notifications.create(string notificationId, NotificationOptions options, function callback)
  99. * @param {[type]} notificationDetails [description]
  100. * @param {Function} callback [description]
  101. */
  102. function GM_notification(notificationDetails, callback) {
  103. chrome.runtime.sendMessage({
  104. notifDetails: {
  105. details: notificationDetails,
  106. callbackFunc: callback
  107. }
  108. });
  109. }
  110.  
  111. function GM_deleteValue(name) {
  112.  
  113. }
  114.  
  115. function GM_listValues() {
  116.  
  117. }
  118.  
  119. /**
  120. * ***********
  121. * For CRX End
  122. * ***********
  123. */
  124. openInTab = GM_openInTab; //GM_openInTab(url, option);
  125. setVal = GM_setValue; // GM_setValue(name, value)
  126. getVal = GM_getValue; // GM_getValue(name, defaultValue)
  127. notification = GM_notification; // GM_notification(text, title, image, onclick)
  128. delVal = GM_deleteValue; // GM_deleteValue(name)
  129. listVals = GM_listValues; // GM_listValues()
  130.  
  131.  
  132. // inject layer.css
  133. $("<link>")
  134. .attr({
  135. rel: "stylesheet",
  136. type: "text/css",
  137. href: config.fontawesome_css
  138. })
  139. .appendTo("head");
  140.  
  141.  
  142. } else {
  143. console.log("in Script ");
  144.  
  145. openInTab = GM_openInTab; //GM_openInTab(url, option);
  146. setVal = GM_setValue; // GM_setValue(name, value)
  147. getVal = GM_getValue; // GM_getValue(name, defaultValue)
  148. notification = GM_notification; // GM_notification(text, title, image, onclick)
  149. delVal = GM_deleteValue; // GM_deleteValue(name)
  150. listVals = GM_listValues; // GM_listValues()
  151.  
  152.  
  153.  
  154. // inject layer.css
  155. $("<link>")
  156. .attr({
  157. rel: "stylesheet",
  158. type: "text/css",
  159. href: config.layer_css
  160. })
  161. .appendTo("head");
  162.  
  163.  
  164. // inject layer.css
  165. $("<link>")
  166. .attr({
  167. rel: "stylesheet",
  168. type: "text/css",
  169. href: config.fontawesome_css
  170. })
  171. .appendTo("head");
  172.  
  173. }
  174.  
  175. /**
  176. * For notification function
  177. *
  178. * text - the text of the notification (required unless highlight is set)
  179. * title - the notificaton title
  180. * image - the image
  181. * highlight - a boolean flag whether to highlight the tab that sends the notfication (required unless text is set)
  182. * silent - a boolean flag whether to not play a sound
  183. * timeout - the time after that the notification will be hidden (0 = disabled)
  184. * ondone - called when the notification is closed (no matter if this was triggered by a timeout or a click) or the tab was highlighted
  185. * onclick - called in case the user clicks the notification
  186. */
  187. function getNotificationDetails(_text, _timeout, _title, _image, _highlight, _silent, _ondone, _onclick) {
  188.  
  189. let details = {
  190. text: _text === undefined ? '' : _text,
  191. title: _title === undefined || _title === null ? config.notificationTitle : _title,
  192. image: _image === undefined || _image === null ? config.icon48 : _image,
  193. highlight: _highlight === undefined || _highlight === null ? true : _highlight,
  194. silent: _silent === undefined || _silent === null ? false : _silent,
  195. timeout: _timeout === undefined || _timeout === null ? 6000 : _timeout,
  196. ondone: _ondone === undefined || _ondone === null ? null : _ondone,
  197. onclick: _onclick === undefined || _onclick === null ? null : _onclick
  198. };
  199. return details;
  200.  
  201. };
  202.  
  203.  
  204. /**
  205. * Determine the browser type
  206. */
  207. function browserType() {
  208. var userAgent = navigator.userAgent; //get browser userAgent string
  209. var isOpera = userAgent.indexOf("Opera") > -1;
  210. if (isOpera) {
  211. return "Opera"
  212. }; //is Opera or not
  213. if (userAgent.indexOf("Firefox") > -1) {
  214. return "FF";
  215. } //is Firefox or not
  216. if (userAgent.indexOf("Chrome") > -1) {
  217. return "Chrome";
  218. }
  219. if (userAgent.indexOf("Safari") > -1) {
  220. return "Safari";
  221. } //is Safari or not
  222. if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {
  223. return "IE";
  224. }; //is IE or not
  225. }
  226. /**
  227. * sleep function
  228. * @param numberMillis -- 要睡眠的毫秒数
  229. */
  230. function sleep(numberMillis) {
  231. var now = new Date();
  232. var exitTime = now.getTime() + numberMillis;
  233. while (true) {
  234. now = new Date();
  235. if (now.getTime() > exitTime)
  236. return;
  237. }
  238. }
  239.  
  240. /**
  241. * Remove duplicate value in array
  242. */
  243. function removeDuplicate(arr) {
  244. let x = new Set(arr);
  245. return [...x];
  246. }
  247. /**
  248. * download resources function
  249. */
  250. function download(name, href) {
  251. var a = document.createElement("a"), //创建a标签
  252. e = document.createEvent("MouseEvents"); //创建鼠标事件对象
  253. e.initEvent("click", false, false); //初始化事件对象
  254. a.href = href; //设置下载地址
  255. a.download = name; //设置下载文件名
  256. a.dispatchEvent(e); //给指定的元素,执行事件click事件
  257. }
  258.  
  259. /**
  260. * Refresh page tips
  261. */
  262. function refreshPage() {
  263. alert("操作完成,请小可爱刷新页面查看结果!!!");
  264. }
  265.  
  266. /**
  267. * cancel action
  268. */
  269. function cancel() {
  270. alert("已取消操作!");
  271. }
  272. /**
  273. * 点击和下载前以弹窗二次确认
  274. * modeName
  275. * return boolean
  276. **/
  277. function popupComfirm(modeName) {
  278. let conf_str = false;
  279. conf_str = confirm("小可爱,你即将执行“" + modeName + "”操作!!!" + "\n\n" +
  280. "根据选择资源数量的不同,会打开相应数量的页面,如果数量较多,请不要惊慌,因为这些页面会自动关闭的哦!!!" + "\n\n" +
  281. "你是否按照上一个提示,进行了相应的操作?" + "\n\n" + "如果是,你是否要开始执行本次操作?");
  282. return conf_str;
  283. }
  284.  
  285. /**
  286. * cleaning data 数据清洗 inputString -> idsArr
  287. */
  288. function cleanData(inputString) {
  289. //去除字符串中的所有空格
  290. inputString.replace(/\s*/g, "");
  291. //去掉首尾的 - 字符
  292. if (inputString.charAt(0) == "-") {
  293. if (inputString.charAt(inputString.length - 1) == "-")
  294. inputString = inputString.substring(1, inputString.length - 1);
  295. else
  296. inputString = inputString.substring(1, inputString.length);
  297. } else if (inputString.charAt(inputString.length - 1) == "-") {
  298. inputString = inputString.substring(0, inputString.length - 1);
  299. }
  300. //console.log(inputString + "/" + inputString.charAt(0) +"/" + inputString.charAt(inputString.length-1));
  301. //分割出要点击的栏号,存入数组,用于映射出对应的资源栏id
  302. let idsArr = inputString.split("-");
  303. //去重并排序
  304. idsArr = removeDuplicate(idsArr).sort();
  305. //去除超出资源栏总数的无效值
  306. let temp = [];
  307. for (let i = 0; i < idsArr.length; i++) {
  308. // console.log("srcBarSum is" + srcBarSum);
  309. if (idsArr[i] <= srcBarSum && idsArr[i] > 0) {
  310. temp.push(idsArr[i]);
  311. // console.log("temp is" + temp);
  312. }
  313. }
  314. // console.log("idsArr is" + idsArr);
  315. return idsArr = temp;
  316.  
  317. }
  318.  
  319. /**
  320. * 根据模式名执行对应的批量处理操作
  321. *
  322. * 点击确认按钮弹出确认弹窗,
  323. * 如果确认执行,则执行点击操作,
  324. * 否则执行 取消操作
  325. */
  326. function batchForMoreSrcBars(modeName, ids) {
  327. if (ids.length == 0)
  328. ids.push(".res-row-box");
  329.  
  330. let isDownloadMode = modeName == "模拟点击" ? "false" : (modeName == "批量下载" ? "true" : "其他");
  331.  
  332. if (popupComfirm(modeName)) {
  333. try {
  334. // console.log(chosenIDs);
  335. let startIndex = $("#head").val();
  336. let endIndex = $("#tail").val();
  337. for (let id of ids) {
  338. // console.log(thisID);
  339. try {
  340. batch(isDownloadMode, id, startIndex, endIndex);
  341. } catch (e) {
  342. console.log(id + "该栏执行异常,跳过执行");
  343. continue;
  344. }
  345. }
  346. } finally {
  347. //点击完成,提示刷新页面
  348. setTimeout(refreshPage, 0);
  349. //置空栏号输入框
  350. $(".indexNum").val("");
  351.  
  352. }
  353. } else {
  354. cancel();
  355. }
  356. }
  357.  
  358. /**
  359. * Click or download in bulk according to
  360. * isDownload : true -> Download Mode ; false -> Click Mode
  361. * thisBarID : 此次要执行的资源栏 id
  362. * startIndex : 此次资源栏中执行的开始资源编号
  363. * endIndex : 此次资源栏中执行的结束资源编号
  364. *
  365. */
  366. function batch(isDownload, thisBarID, startIndex, endIndex) {
  367. //let isDownloadMesg = isDownload == "false" ? "模拟点击" : "批量下载";
  368.  
  369. // 以下五个等价,实现相同功能,但写法是逐步优化
  370. // var list = document.getElementsByClassName("res-row-open-enable");
  371. // var list = $(".res-row-open-enable");
  372. // var list = $(".hide-div").children();
  373. // var list = $(".res-row-box").children(".hide-div").children();
  374. let list = $(thisBarID).children(".hide-div").children();
  375. let succNum = 0;
  376. let failNum = 0;
  377. let tempUrl;
  378. let win;
  379.  
  380. let actualStartIndex = startIndex <= list.length && startIndex > 0 ? startIndex : (startIndex <= 0 ? 1 : list.length); //小于0则为 1 ; 大于 最大值 则为 最大值
  381. let actualEndIndex = endIndex <= list.length && endIndex > 0 ? endIndex : (endIndex <= 0 ? 1 : list.length); //输入值超出资源总数的值,则将输入值置为总数的值
  382. if (actualStartIndex > actualEndIndex) {
  383. //console.log("here");
  384. alert("小可爱😀,你的起始结束值写反了哟!");
  385. return;
  386. }
  387. // console.log("actualStartIndex: " + actualStartIndex);
  388. // console.log("actualEndIndex: " + actualEndIndex);
  389. // list 存在并不为空
  390. if (null == list || list.length == 0) {
  391. console.log(thisBarID + "对应的资源栏为空");
  392. } else {
  393.  
  394. for (let i = actualStartIndex - 1; i < actualEndIndex; i++) {
  395. // console.log(i);
  396. // console.log(list);
  397. // console.log(list[i]);
  398. try {
  399.  
  400. tempUrl = list[i].getAttribute("data-href");
  401. if (null == tempUrl || tempUrl == "") {
  402.  
  403. console.log("资源栏:" + thisBarId + "的第 " + (i + 1) + " 条资源未获取到URL");
  404.  
  405. } else {
  406.  
  407. win = window.open(tempUrl);
  408. if (isDownload == 'false') {
  409. sleep(100); //睡眠,是为了确保每个资源都被正常获取
  410. win.close();
  411. }
  412. succNum++;
  413. // console.log(tempUrl);
  414.  
  415. }
  416. } catch (e) {
  417. console.log(e.name + ": " + e.message);
  418. console.log("资源栏:" + thisBarId + "的第 " + (i + 1) + " 条未成功执行 ;URL : " + list[i].getAttribute("data-href"));
  419. failNum++;
  420. continue;
  421. }
  422. }
  423.  
  424. }
  425. console.log("共检索到 " + list.length + "条; 成功执行 " + succNum + " 次! 失败 " + failNum + " 次! 操作范围:从第 " + actualStartIndex + " 条 至 第 " + actualEndIndex + " 条。");
  426. }
  427.  
  428. /**
  429. * click all resources in two ways according to 'isPositive'
  430. */
  431. function clickAll(isPositive) {
  432.  
  433. let isPositiveMesg = isPositive == "true" ? "正序点击" : "倒序点击";
  434.  
  435. let conf_str = false;
  436. conf_str = confirm("小可爱,你即将执行“" + isPositiveMesg +
  437. "全部资源”操作,如果资源量较大(> 1000),耗时就会较久,打开的页面也会较多哦!不过都会自动关闭的哦!!!" + "\n\n" +
  438. "小可爱,资源较多时,还请三思啊!!!" + "\n\n" + "你是否要执行?");
  439. if (conf_str) {
  440. let list = document.getElementsByClassName("res-row-open-enable");
  441. let succNum = 0;
  442. let failNum = 0;
  443. let tempUrl;
  444. let win;
  445. if (isPositive == "true") {
  446. for (let i = 0; i < list.length; i++) {
  447. try {
  448. tempUrl = list[i].getAttribute("data-href");
  449. win = window.open(tempUrl);
  450. sleep(100); //睡眠,是为了确保每个资源都被正常获取
  451. win.close();
  452. succNum++;
  453. // console.log(tempUrl);
  454. } catch (e) {
  455. console.log(e.name + ": " + e.message);
  456. console.log("该条未成功执行 ;URL : " + list[i].getAttribute("data-href"));
  457. failNum++;
  458. continue;
  459. }
  460. }
  461. } else {
  462. for (let i = list.length - 1; i >= 0; i--) {
  463. try {
  464. tempUrl = list[i].getAttribute("data-href");
  465. win = window.open(tempUrl);
  466. sleep(100); //睡眠,是为了确保每个资源都被正常获取
  467. win.close();
  468. succNum++;
  469. // console.log(tempUrl);
  470. } catch (e) {
  471. console.log(e.name + ": " + e.message);
  472. console.log("该条未成功执行 ;URL : " + list[i].getAttribute("data-href"));
  473. failNum++;
  474. continue;
  475. }
  476. }
  477. }
  478. console.log(isPositiveMesg + ": 共检索到 " + list.length + "条; 成功执行 " + succNum + " 次! 失败 " + failNum + " 次!");
  479. setTimeout(refreshPage, 0);
  480. } else {
  481. alert("已取消操作!");
  482. }
  483. }
  484.  
  485. /**
  486. * open a new tab according the url and execute callback function
  487. */
  488. function newTabAlert(forWhat, url, option, callback) {
  489. if (config.isCRX)
  490. openInTab(url, option, forWhat);
  491. else
  492. openInTab(url, option);
  493. if (typeof callback === "function") {
  494. callback();
  495. }
  496. }
  497.  
  498.  
  499.  
  500. /******************************************
  501. * play videos
  502. *
  503. */
  504.  
  505. let playVideoConfig = {
  506. isContinuous: false,
  507. isPlayAll: false,
  508. isPlayPart: false,
  509. videoDuration: '',
  510. };
  511.  
  512.  
  513.  
  514. /**********************************************
  515. * Play all videos continuously
  516. */
  517.  
  518. let arr = [];
  519. let count = 0;
  520. let interval;
  521. let timeout;
  522. let intervalTime = 4000; //millisecond
  523. let isContinuousPaly = false;
  524. let rate = 1; // <=10
  525. let weight = 1000 / rate;
  526. let currentVideoIndex = 0;
  527. let nextVideoIndex = 0; //当前第几个视频
  528. let bufferTime = 10000; // millisecond
  529. let maxRate = 8;
  530. let log = '';
  531.  
  532. //将所有视频资源存入数组,以作点击使用
  533. let a = $("div[data-mime='video']");
  534. let tempArr = Object.keys(a);
  535. let tpArr = tempArr.slice(0, tempArr.length - 2);
  536. tpArr.forEach((key) => {
  537. //console.log(key, a[key]);
  538. arr.push(a[key]);
  539. });
  540. //console.log(arr.length);
  541. // for (let a in arr) {
  542. // console.log(a);
  543. // }
  544. //
  545. playVideoConfig.videoDuration = $('.video-duration');
  546.  
  547.  
  548. function onContinuousPlayFunc() {
  549.  
  550. if (playVideoConfig.isContinuous) {
  551. layer.msg('【无效操作】 : 连播功能已开启');
  552. return;
  553. }
  554.  
  555. playVideoConfig.isContinuous = true;
  556.  
  557. if (typeof ($("#continuousPlay").attr("class")) != "undefined") {
  558. let text = "连续播放已开启,无需重复开启";
  559. //alert(text);
  560. notification(getNotificationDetails(text), null);
  561. //layer.msg("test");
  562. return;
  563. }
  564.  
  565. alert("请先关闭 【 Win10 专注助手 】 再使用,否则无法正常提示信息 \n\n 提示:在通知托盘中关闭");
  566.  
  567.  
  568. // $('<div id = "continuousPlay" class="mejs__button">\
  569. // <button type="button" aria-controls="mep_0" title="开始连续播放" aria-label="Play" tabindex="0"></button>\
  570. // </div>\
  571. // <div id = "stopContinuousPlay" class="mejs__button mejs__playpause-button mejs__pause">\
  572. // <button type="button" aria-controls="mep_0" title="暂停连续播放" aria-label="Pause" tabindex="0"></button>\
  573. // </div>\
  574. // <div id = "continuousPlayN" class="mejs__button">\
  575. // <button type="button" aria-controls="mep_0" title="开始连续播放(n)" aria-label="Play" tabindex="0"></button>\
  576. // </div>\
  577. // <div id = "stopContinuousPlayN" class="mejs__button mejs__playpause-button mejs__pause">\
  578. // <button type="button" aria-controls="mep_0" title="暂停连续播放(n)" aria-label="Pause" tabindex="0"></button>\
  579. // </div>\
  580. // ').insertAfter(".mejs__fullscreen-button");
  581.  
  582. $('<div id="helper-btn" class="content-center" style="background-color:rgba(255, 255, 255, 0.5);">\
  583. <span id="continuousPlayAll" class="video-btn content-center"><i class="fa fa-play-circle" aria-hidden="true" style="cursor:pointer"></i></span>\
  584. <span id="stopContinuousPlayAll" class="video-btn content-center"><i class="fa fa-stop-circle" aria-hidden="true" style="cursor:pointer"></i></span>\
  585. <span id="continuousPlayPart" class="video-btn content-center"><i class="fa fa-play" aria-hidden="true" style="cursor:pointer"></i></span>\
  586. <span id="stopContinuousPlayPart" class="video-btn content-center"><i class="fa fa-stop" aria-hidden="true" style="cursor:pointer"></i></span>\
  587. </div>').insertBefore("#preview-video");
  588.  
  589. //For all
  590. $("#continuousPlayAll").click(() => {
  591. startContinuousPlayAll();
  592. });
  593.  
  594. $("#stopContinuousPlayAll").click(() => {
  595. stopContinuousPlayAll();
  596.  
  597. });
  598.  
  599.  
  600. //For part
  601. $("#continuousPlayPart").click(() => {
  602. startContinuousPlayForPart();
  603. });
  604.  
  605. $("#stopContinuousPlayPart").click(() => {
  606. stopContinuousPlayForPart();
  607. });
  608.  
  609.  
  610.  
  611. let txt = "连续播放已开启,请到视频播放页面使用";
  612. notification(getNotificationDetails(txt), null);
  613. }
  614.  
  615.  
  616. /**
  617. * close continuous play
  618. *
  619. */
  620. function offContinuousPlayFunc() {
  621.  
  622. if (!playVideoConfig.isContinuous) {
  623. layer.msg('【无效操作】 : 连播功能已关闭');
  624. return;
  625. }
  626.  
  627. $('#continuousplayAll, #stopContinusPlayAll, #continuousPlayPart, #stopContinusPlayPart').unbind();
  628. $('#helper-btn').remove();
  629. if (playVideoConfig.isPlayAll) {
  630. stopContinuousPlayAll();
  631. }
  632. if (playVideoConfig.isPlayPart) {
  633. stopContinuousPlayForPart();
  634. }
  635. playVideoConfig.isContinuous = false;
  636. layer.msg('连播功能已关闭!');
  637. }
  638.  
  639.  
  640.  
  641. /**
  642. * For all
  643. * steps that must be taken when start playing continuously
  644. */
  645. function startContinuousPlayAll() {
  646.  
  647. if (playVideoConfig.isPlayPart) {
  648. layer.msg('【无效操作】 : 正常连播进行中...');
  649. return;
  650. }
  651. if (isContinuousPaly) {
  652. layer.msg("【无效操作】:全部连播进行中...");
  653. return;
  654. }
  655.  
  656.  
  657. if (playBySpecifiedRateAndNotify()) {
  658. $('.video-duration').remove();
  659. playVideoConfig.isPlayAll = true;
  660. isContinuousPaly = true;
  661. layer.msg('禁用进度条', function () {
  662. clickDiv();
  663. });
  664.  
  665. } else {
  666. notification(getNotificationDetails("已取消本次操作!"), null);
  667. }
  668. }
  669.  
  670. /**
  671. * For all
  672. * steps that must be taken when stopping playing continuously
  673. */
  674. function stopContinuousPlayAll() {
  675.  
  676.  
  677. if (playVideoConfig.isPlayPart) {
  678. stopContinuousPlayForPart();
  679. playVideoConfig.isPlayPart = false;
  680. }
  681.  
  682. if (!isContinuousPaly) {
  683. layer.msg("【无效操作】:全部连播未执行");
  684. return;
  685. }
  686. // console.log("llllllllll:"+$('.video-duration') );
  687. if ($('.video-duration').length == 0) {
  688. $(playVideoConfig.videoDuration).insertAfter('#mep_0');
  689. }
  690. isContinuousPaly = false;
  691. playVideoConfig.isPlayAll = false;
  692. //停掉当前还未执行完的 interval timeout
  693. clearInterval(interval);
  694. clearTimeout(timeout);
  695. layer.msg('全部连播已关闭');
  696. let stopContinusPlayText = "已退出连续播放模式,但保留了关闭视频即可看完功能;\n下一次连续播放从第 " + (nextVideoIndex + 1) + " 个视频开始。";
  697. //alert(stopContinusPlayText);
  698. notification(getNotificationDetails(stopContinusPlayText), null);
  699. }
  700.  
  701. /**
  702. * For part
  703. * start playing continuously part of all the specified videos
  704. */
  705. function startContinuousPlayForPart() {
  706.  
  707. if (playVideoConfig.isPlayAll) {
  708. layer.msg('【无效操作】 : 全部连播进行中...');
  709. return;
  710. }
  711. if (videoConfig.isContinuousPaly) {
  712. layer.msg("【无效操作】:正常连播进行中...");
  713. return;
  714. }
  715.  
  716.  
  717. playVideoConfig.isPlayPart = true;
  718. videoConfig.isContinuousPaly = true;
  719. play(videoConfig.currentVideoDivs);
  720. }
  721.  
  722. //部分连播
  723. //开始时不需要设置播放速度,
  724. //结束时不需要提示下次播放位置
  725. //只需开始/结束提即可
  726. /**
  727. * For part
  728. * stop playing continuously part of all the specified videos
  729. */
  730. function stopContinuousPlayForPart() {
  731.  
  732.  
  733. if (playVideoConfig.isPlayAll) {
  734. stopContinuousPlayAll();
  735. playVideoConfig.isPlayAll = false;
  736. }
  737.  
  738.  
  739. if (!videoConfig.isContinuousPaly) {
  740. layer.msg("【无效操作】:正常连播未执行");
  741. return;
  742. }
  743.  
  744. videoConfig.isContinuousPaly = false;
  745. playVideoConfig.isPlayPart = false;
  746.  
  747. let video = document.querySelector('video');
  748. let isPaused = video.paused;
  749. if (isPaused) {
  750. video.play();
  751. video.pause();
  752. } else {
  753. video.pause();
  754. video.play();
  755. }
  756. }
  757.  
  758. /**
  759. * unlock progress bar and click this div
  760. * return the index of layer
  761. */
  762. function unlockBarAndClickDiv(div, func) {
  763. layer.msg('解锁进度条中...', {
  764. time: 1500,
  765. },
  766. function () {
  767. let info = '未上锁';
  768. if ($(div).attr('data-drag') == 'N') {
  769.  
  770. $(div).attr('data-drag', 'Y');
  771. info = '已解锁!';
  772.  
  773. }
  774. layer.msg(info, {
  775. time: 1500
  776. },
  777. function () {
  778. $(div).trigger('click');
  779. if (typeof func === "function") {
  780. func();
  781. }
  782. });
  783.  
  784. });
  785. }
  786.  
  787. /**
  788. * is or not a number
  789. * @param {[type]} val [description]
  790. * @return {Boolean} [description]
  791. */
  792. function isNumber(val) {
  793.  
  794. var regPos = /^\d+(\.\d+)?$/; //非负浮点数
  795. var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数
  796. if (regPos.test(val) || regNeg.test(val)) {
  797. return true;
  798. } else {
  799. return false;
  800. }
  801.  
  802. }
  803.  
  804. /**
  805. * play next videos according to the specified rate inputed by user and notify user
  806. */
  807. function playBySpecifiedRateAndNotify() {
  808.  
  809. let inputRate = prompt("以几倍速度进行连续播放呀🧐(最高" + maxRate + "倍哦!)建议1.8倍最佳🤭");
  810. //console.log(inputRate);
  811. if (inputRate == null) {
  812. return false;
  813. }
  814. if (!isNumber(inputRate)) {
  815. let text = "啥❓ 你输入了啥,那是数字吗?\n 再输一次吧,别输出咯!😀";
  816. notification(getNotificationDetails(text), null);
  817. return false;
  818. }
  819. rate = inputRate <= 0 ? 1 : (inputRate > maxRate ? maxRate : inputRate);
  820. weight = 1000 / rate;
  821. let text = "连续播放已开始!\n将以 " + rate + " 倍速 播放 " + (arr.length - nextVideoIndex) + " 个视频。";
  822. //console.log(text);
  823. notification(getNotificationDetails(text), null);
  824. return true;
  825.  
  826. }
  827. /**
  828. * edit those value about duration before sending ajax
  829. */
  830. function send() {
  831. $.ajaxSetup({
  832. beforeSend: function () {
  833. let argsData = arguments[1].data
  834. let falseArgsData = "";
  835. let falseVal;
  836. for (let k in argsData) {
  837.  
  838. if (k.includes("watch_to")) {
  839. //console.log("before: " + k + " : " + argsData[k]);
  840. falseVal = argsData.duration;
  841. //console.log("after: " + k + " : " + falseVal);
  842. } else {
  843. falseVal = argsData[k];
  844. }
  845. falseArgsData = falseArgsData + "&" + k + "=" + falseVal;
  846. }
  847. arguments[1].data = falseArgsData.substring(1, falseArgsData.length);
  848. },
  849. processData: false,
  850. complete: function () {
  851. console.log("send completed");
  852. }
  853. });
  854. }
  855. /**
  856. * trigger the click action of the current DIV
  857. *
  858. */
  859. function clickDiv() {
  860. currentVideoIndex = count++;
  861. nextVideoIndex = currentVideoIndex + 1;
  862.  
  863. if (isContinuousPaly == false) {
  864. //console.log("在播放第 " + (nextVideoIndex) + " 个视频时退出了连续播放");
  865. return;
  866. }
  867.  
  868. //第一次使用连续播放开启 关闭即可看完
  869. if (currentVideoIndex == 0) {
  870. send();
  871. }
  872.  
  873. if (currentVideoIndex < arr.length) {
  874. // $(arr[currentVideoIndex]).trigger("click");
  875. unlockBarAndClickDiv(arr[currentVideoIndex], playThisVideo);
  876. // setTimeout(function() {layer.close(index)}, videoConfig.loadingTime);
  877. //console.log(currentVideoIndex + " : " + arr[currentVideoIndex]);
  878. // playThisVideo();
  879. } else {
  880. setTimeout(() => {
  881. clearInterval(interval);
  882. }, 0);
  883. //console.log("currentVideoIndex: " + currentVideoIndex);
  884. //关掉最后一个视频
  885. $(".close-window").trigger("click");
  886. alert("连续播放结束, 共连续播放了 " + arr.length + " 个视频,即将刷新页面");
  887. location.reload();
  888. }
  889. }
  890.  
  891. /**
  892. * play the current video until it is over and play the next video
  893. */
  894. function playThisVideo() {
  895. if (currentVideoIndex >= arr.length) {
  896. return;
  897. }
  898. let duration;
  899. let currentTime;
  900. setTimeout(() => {
  901. let video = document.querySelector('video');
  902. let duration = video.duration;
  903. let currentTime = video.currentTime;
  904. let isPaused = video.paused;
  905. //console.log(isPaused ? "暂停" : "未停");
  906. // if video has paused then play the video
  907. if (isPaused) {
  908. $(".mejs__replay").trigger("click");
  909. //console.log("开始播放");
  910. }
  911. setTimeout(() => {
  912. //console.log(document.querySelector('video').paused ? "暂停" : "未停");
  913. }, 500);
  914. video.playbackRate = rate;
  915.  
  916. //second --> millisecond
  917. let remain = (duration - currentTime) * weight;
  918.  
  919. console.log("该视频剩余播放时长 :" + remain + " 毫秒");
  920.  
  921. //停掉上一个interval timeout
  922. clearInterval(interval);
  923. clearTimeout(timeout);
  924.  
  925. //is NaN
  926. if (duration != duration || currentTime != currentTime || remain != remain) {
  927.  
  928. stopContinuousPlayAll();
  929. notification(getNotificationDetails("执行异常,已停止本次连播。" +
  930. "\n下一次连续播放从第 " + (nextVideoIndex + 1) + " 个视频开始。", 10000), null);
  931. return;
  932.  
  933. }
  934.  
  935.  
  936. interval = setInterval(clickDiv, remain + intervalTime);
  937.  
  938. timeout = setTimeout(() => {
  939. //console.log("当前视频播放到:" + document.querySelector('video').currentTime);
  940. $(".close-window").trigger("click");
  941. //console.log("关闭第" + nextVideoIndex + "个视频");
  942. //console.log(intervalTime + " 毫秒后播放下一个视频");
  943. }, remain);
  944.  
  945. }, bufferTime);
  946. }
  947.  
  948.  
  949. /**
  950. * play all the videos since this video
  951. */
  952. let videoConfig = {
  953. videoSum: 0,
  954. currentVideoId: '',
  955. currentVideoDivs: arr,
  956. isContinuousPaly: false,
  957. loadingTime: 6000,
  958. rate: 1,
  959.  
  960. }
  961.  
  962. $("div[data-mime='video']").each(function (i, e) {
  963. let ts = $(this);
  964. ts.attr('id', 'vdoId_' + i);
  965. ts.bind('click', function (event) {
  966. /* Act on the event */
  967. let id = videoConfig.currentVideoId = ts.attr('id');
  968. let split = id.split('_');
  969. let newFirstIndex = Number(split[1]);
  970. videoConfig.currentVideoDivs = arr.slice(newFirstIndex);
  971. //console.log(videoConfig.currentVideoDivs);
  972.  
  973. });
  974. videoConfig.videoSum = ++i;
  975. });
  976.  
  977.  
  978. //每拿一个阻塞一次,
  979. function play(videosArr) {
  980.  
  981. layer.msg(
  982. "连播开始!(共" + videosArr.length + "个)", {
  983. time: 3000
  984. },
  985.  
  986. async function () {
  987. let isOver = true;
  988. for (let i = 0; isOver && i < videosArr.length; i++) {
  989. //console.log("time:" + i);
  990. isOver = await playOne(videosArr[i]);
  991.  
  992. }
  993. //console.log("Done all");
  994. videoConfig.isContinuousPaly = false;
  995. layer.msg("连播结束!");
  996. }
  997. );
  998. }
  999.  
  1000.  
  1001.  
  1002. function playOne(div) {
  1003. unlockBarAndClickDiv(div);
  1004. let index = layer.load();
  1005.  
  1006. // if(document.querySelector('video').readyState == 4){
  1007. // layer.msg("OK");
  1008. // };
  1009. return new Promise(resolve => {
  1010. setTimeout(() => {
  1011.  
  1012. //close load
  1013. layer.close(index);
  1014.  
  1015. let video = document.querySelector('video');
  1016.  
  1017. let onPause = function () {
  1018. let a = video.currentTime == 0 || video.currentTime == video.duration;
  1019. let b = videoConfig.isContinuousPaly;
  1020. if (b && a) {
  1021. //视频播完会回到开头如果没有回到开头应该在结尾
  1022. resolve(true);
  1023. //console.log("连播&结束");
  1024. } else if (!b && !a) {
  1025. //如果按下暂停前关闭了连续播放 == 结束本次列表循环
  1026. resolve(false);
  1027. //console.log("play stopped");
  1028. } else if (b && !a) {
  1029. //如果还在连续播放但是按下暂停 == 暂停 ,什么也不做
  1030. //console.log("play blocked");
  1031. } else if (!b && a) {
  1032. //不再连播但播放结束
  1033. resolve(false);
  1034. //console.log("不连播&结束");
  1035. }
  1036. }
  1037.  
  1038. video.removeEventListener('pause', onPause, false);
  1039. // let duration = video.duration;
  1040. // let currentTime = video.currentTime;
  1041. let isPaused = video.paused;
  1042. //console.log(isPaused ? "本是暂停" : "本是播放");
  1043. // if video has paused then play the video
  1044. if (isPaused) {
  1045. $(".mejs__replay").trigger("click");
  1046. //console.log("暂停-》开始播放");
  1047. }
  1048. setTimeout(() => {
  1049. //console.log(document.querySelector('video').paused ? "依旧暂停" : "已打开播放");
  1050. }, 500);
  1051. video.playbackRate = keyboardEvent.currentSpeed;
  1052. // video.addEventListener("ended", function() {
  1053. // resolve(true);
  1054. // console.log("this over");
  1055. // });
  1056. video.addEventListener('pause', onPause);
  1057.  
  1058. }, videoConfig.loadingTime);
  1059.  
  1060. });
  1061.  
  1062. }
  1063.  
  1064.  
  1065. /**********************************************
  1066. * keyMap module
  1067. */
  1068. let keyboardEvent = {
  1069. keyBindings: [],
  1070. speedStep: 0,
  1071. rewindTime: 0,
  1072. advanceTime: 0,
  1073. fastSpeed: 0,
  1074. slowerKeyCode: 0,
  1075. fasterKeyCode: 0,
  1076. rewindKeyCode: 0,
  1077. advanceKeyCode: 0,
  1078. resetKeyCode: 0,
  1079. fasterKeyCode: 0,
  1080. currentSpeed: 1.0,
  1081. functionKey: {
  1082. keyMap: 0,
  1083. playAll: 0,
  1084. stopPlayAll: 0,
  1085. playPart: 0,
  1086. stopPlayPart: 0,
  1087. onContinuousPlayFunc: 0,
  1088. offContinuousPlayFunc: 0,
  1089. showTips: 0
  1090.  
  1091. },
  1092. keyMapInfo: ``,
  1093. keyMapDetail: []
  1094.  
  1095. };
  1096.  
  1097.  
  1098. // for video
  1099. keyboardEvent.keyBindings.push({
  1100. action: "slower",
  1101. key: Number(keyboardEvent.slowerKeyCode) || 83,
  1102. value: Number(keyboardEvent.speedStep) || 0.1,
  1103. force: false,
  1104. predefined: true
  1105. }); // default S
  1106. keyboardEvent.keyBindings.push({
  1107. action: "faster",
  1108. key: Number(keyboardEvent.fasterKeyCode) || 87,
  1109. value: Number(keyboardEvent.speedStep) || 0.1,
  1110. force: false,
  1111. predefined: true
  1112. }); // default: W
  1113. keyboardEvent.keyBindings.push({
  1114. action: "rewind",
  1115. key: Number(keyboardEvent.rewindKeyCode) || 65,
  1116. value: Number(keyboardEvent.rewindTime) || 10,
  1117. force: false,
  1118. predefined: true
  1119. }); // default: A
  1120. keyboardEvent.keyBindings.push({
  1121. action: "advance",
  1122. key: Number(keyboardEvent.advanceKeyCode) || 68,
  1123. value: Number(keyboardEvent.advanceTime) || 10,
  1124. force: false,
  1125. predefined: true
  1126. }); // default: D
  1127. keyboardEvent.keyBindings.push({
  1128. action: "reset",
  1129. key: Number(keyboardEvent.resetKeyCode) || 82,
  1130. value: 1.0,
  1131. force: false,
  1132. predefined: true
  1133. }); // default: R
  1134. keyboardEvent.keyBindings.push({
  1135. action: "fast",
  1136. key: Number(keyboardEvent.fastKeyCode) || 71,
  1137. value: Number(keyboardEvent.fastSpeed) || 1.8,
  1138. force: false,
  1139. predefined: true
  1140. }); // default: G
  1141.  
  1142.  
  1143. // for functions
  1144. keyboardEvent.keyBindings.push({
  1145. action: 'keyMap',
  1146. key: Number(keyboardEvent.functionKey.keyMap) || 77
  1147. }); // M
  1148. keyboardEvent.keyBindings.push({
  1149. action: 'playAll',
  1150. key: Number(keyboardEvent.functionKey.playAll) || 90
  1151. }); // Z
  1152. keyboardEvent.keyBindings.push({
  1153. action: 'stopPlayAll',
  1154. key: Number(keyboardEvent.functionKey.stopPlayAll) || 88
  1155. }); // X
  1156. keyboardEvent.keyBindings.push({
  1157. action: 'playPart',
  1158. key: Number(keyboardEvent.functionKey.playPart) || 67
  1159. }); // C
  1160. keyboardEvent.keyBindings.push({
  1161. action: 'stopPlayPart',
  1162. key: Number(keyboardEvent.functionKey.stopPlayPart) || 86
  1163. }); // V
  1164. keyboardEvent.keyBindings.push({
  1165. action: 'onContinuousPlayFunc',
  1166. key: Number(keyboardEvent.functionKey.onContinuousPlayFunc) || 66
  1167. }); // B
  1168. keyboardEvent.keyBindings.push({
  1169. action: 'offContinuousPlayFunc',
  1170. key: Number(keyboardEvent.functionKey.offContinuousPlayFunc) || 78
  1171. }); // N
  1172.  
  1173.  
  1174. keyboardEvent.keyBindings.push({
  1175. action: 'showTips',
  1176. key: Number(keyboardEvent.functionKey.showTips) || 84
  1177. }); // T
  1178.  
  1179.  
  1180. /**
  1181. * get the content of the action specified
  1182. * the action bound to some event
  1183. * @return json
  1184. */
  1185. function getKeyBindingsByAction(action) {
  1186.  
  1187. let item = keyboardEvent.keyBindings.find(item => item.action === action);
  1188. return item;
  1189.  
  1190. }
  1191.  
  1192. /**
  1193. * get the value by specified action and keyname
  1194. * @param {string} action [the action bound to some event]
  1195. * @param {string} keyname
  1196. * @return {[type]}
  1197. */
  1198. function getValueByActionAndKeyname(action, keyname) {
  1199. return getKeyBindingsByAction(action)[keyname];
  1200. }
  1201.  
  1202. /**
  1203. * [get all values by specified keyname ]
  1204. * @return {[array]} [all values]
  1205. */
  1206. function getAllValuesByKeyname(keyname) {
  1207. let all = [];
  1208. let arr = keyboardEvent.keyBindings;
  1209. for (let i in arr) {
  1210. let x = arr[i];
  1211. /**
  1212. * access value by variable key
  1213. * x.keyname ==> x[keyname]
  1214. */
  1215. all.push(x[keyname]);
  1216. }
  1217. // console.log('all:'+ all);
  1218. return all;
  1219. }
  1220.  
  1221. function changeKeycode(keycodeArr, toLowercase) {
  1222.  
  1223. let arr = [];
  1224. for (let i in keycodeArr) {
  1225. // if (toLowercase) {
  1226. // arr.push(keycodeArr[i] + 32);
  1227. // }else{
  1228. // arr.push(keycodeArr[i] - 32);
  1229. // }
  1230. toLowercase == true ? arr.push(keycodeArr[i] + 32) : arr.push(keycodeArr[i] - 32);
  1231.  
  1232. }
  1233. //console.log(keycodeArr + '****' + arr);
  1234.  
  1235. return arr;
  1236.  
  1237. }
  1238.  
  1239. /**
  1240. * initialize keyboardEvent: keyMapInfo keyMapDetail
  1241. * @type {[type]}
  1242. */
  1243.  
  1244. keyboardEvent.keyMapDetail = [
  1245.  
  1246. ['强制关闭Chrome', 'Alt + F4'],
  1247. ['查看快捷键', 'shift + m'],
  1248. ['弹出提示', 'shift + t'],
  1249. [`视频加速 (+${getKeyBindingsByAction('faster').value})`, 'W'],
  1250. [`视频减速 (-${getKeyBindingsByAction('slower').value})`, 'S'],
  1251. [`视频快退 ${getKeyBindingsByAction('rewind').value}s`, 'A'],
  1252. [`视频快进 ${getKeyBindingsByAction('advance').value}s`, 'D'],
  1253. [`最佳倍速 ${getKeyBindingsByAction('fast').value})`, 'G'],
  1254. [`重置倍速 ${getKeyBindingsByAction('reset').value})`, 'R'],
  1255. ['开启连播', 'shift + b'],
  1256. ['关闭连播', 'shift + n'],
  1257. ['开始正常连播', 'shift + c'],
  1258. ['结束正常连播', 'shift + v'],
  1259. ['开始全部连播', 'shift + z'],
  1260. ['结束全部连播', 'shift + x']
  1261.  
  1262. ];
  1263. //获取 快捷键列表
  1264. function getKeyMapView() {
  1265. let viewArr = keyboardEvent.keyMapDetail.map((item) => {
  1266. return `<p class="content-center"><span class="keyMap-name"> ${item[0]} </span> <span class="keyMap-value"> ${item[1]} </span></p>`
  1267. });
  1268. return viewArr.join(' ');
  1269. }
  1270.  
  1271. keyboardEvent.keyMapInfo = `
  1272. <div id="keyMapInfo">
  1273. <p class="content-center keyMap-head"><span class="keyMap-name">功能</span><span class="keyMap-value">快捷键</span></p>
  1274. <hr>
  1275. ${getKeyMapView()}
  1276. </div>
  1277. `;
  1278.  
  1279. /**
  1280. * bind keyboard eventListener to document
  1281. */
  1282. let lastTimeStamp = 0;
  1283. let isSameKey = false;
  1284. let lastKeyCode = 0;
  1285. let recent2KeysInterval = 0;
  1286. $(document).bind('keypress', function (event) {
  1287. /* 禁止频繁操作 */
  1288. let curTimeStamp = event.timeStamp;
  1289. recent2KeysInterval = curTimeStamp - lastTimeStamp;
  1290. lastTimeStamp = curTimeStamp;
  1291. if (recent2KeysInterval < 200) {
  1292. layer.msg("操作过于频繁");
  1293. return;
  1294. }
  1295.  
  1296. /* Act on the event */
  1297. let keyCode = event.keyCode;
  1298. let altKey = event.altKey;
  1299. let ctrlKey = event.ctrlKey;
  1300. let shiftKey = event.shiftKey;
  1301. //console.log("keyCode:" + keyCode);
  1302.  
  1303. /* 记录最近两次按下是否为同一个 key */
  1304. isSameKey = lastKeyCode == keyCode ? true : false;
  1305. lastKeyCode = keyCode;
  1306.  
  1307.  
  1308. let lowercase = changeKeycode(getAllValuesByKeyname('key').slice(0, 6), true);
  1309. // console.log('[119, 115, 97, 100, 114, 103]:' + lowercase);
  1310. let funcKeyLowercase = changeKeycode(getAllValuesByKeyname('key').slice(6), true);
  1311. // console.log("[109, 122, 120, 99, 118, 98, 110]:" + funcKeyLowercase);
  1312. let funcKeyUppercase = getAllValuesByKeyname('key').slice(6);
  1313. // console.log("[77, 90, 88, 67, 86, 66, 78]:" + funcKeyUppercase);
  1314. let playVdoFuncKeyLowercase = getAllValuesByKeyname('key').slice(7, 11);
  1315.  
  1316. // shift + lowercase => uppercase 小写键盘
  1317. let shiftAndLowercase = shiftKey && ((funcKeyUppercase.find(item => item === keyCode) === undefined ? false : true));
  1318. // shift + uppercase => lowercase 大写键盘
  1319. let shiftAndUppercase = shiftKey && ((funcKeyLowercase.find(item => item === keyCode) === undefined ? false : true));
  1320.  
  1321. let shiftAndPlayVdoLowercase = shiftKey && ((playVdoFuncKeyLowercase.find(item => item === keyCode) === undefined ? false : true));
  1322.  
  1323. if (!document.querySelector('video').paused) {
  1324.  
  1325. if (lowercase.find(item => item === keyCode)) {
  1326. //console.log("is pause:"+ document.querySelector('video').paused);
  1327. layer.msg('请打开大写键盘 以使用 【视频控件】');
  1328. return;
  1329. }
  1330. if (shiftAndUppercase) {
  1331. layer.msg('请关闭大写键盘 以使用完整的快捷键功能');
  1332. return;
  1333. }
  1334. if (shiftAndPlayVdoLowercase) {
  1335. if (!playVideoConfig.isContinuous) {
  1336. layer.msg('请先开启连播功能');
  1337. return;
  1338. }
  1339.  
  1340. }
  1341.  
  1342. } else if (document.querySelector('video').paused) {
  1343.  
  1344. if (shiftAndUppercase) {
  1345. layer.msg('请关闭大写键盘!以使用完整的快捷键功能');
  1346. return;
  1347. }
  1348. //四个连播功能(ZXCV)在没有开启连播时,提醒开启连播功能
  1349. if (shiftAndPlayVdoLowercase) {
  1350. if (!playVideoConfig.isContinuous) {
  1351. layer.msg('请先开启连播功能');
  1352. return;
  1353. }
  1354. }
  1355. if (!shiftAndLowercase) {
  1356. return;
  1357. }
  1358.  
  1359. }
  1360.  
  1361.  
  1362. let item = keyboardEvent.keyBindings.find(item => item.key === keyCode);
  1363. if (item) {
  1364.  
  1365. let video = document.querySelector('video');
  1366. doAction(item, video);
  1367.  
  1368. }
  1369.  
  1370. });
  1371.  
  1372.  
  1373.  
  1374. /**
  1375. * [doAction description]
  1376. * @param {[type]} item [that event triggered]
  1377. * @param {[type]} video [description]
  1378. */
  1379. function doAction(item, video) {
  1380.  
  1381. let action = item.action;
  1382. let value = item.value;
  1383. let num = (video.playbackRate).toFixed(1);
  1384.  
  1385. /**
  1386. * send a record ( special Keys )
  1387. */
  1388. if (keyboardEventMap.has(action)) {
  1389.  
  1390. if (
  1391. !(specialKeyboardEventMap.has(action)
  1392. && isSameKey
  1393. && recent2KeysInterval < statConfig.specialKeysInterval)
  1394. ) {
  1395. // console.log('not special keys')
  1396. // console.log(specialKeyboardEventMap.has(action));
  1397. // console.log(isSameKey);
  1398. // console.log(recent2KeysInterval);
  1399. record(keyboardEventMap.get(action))
  1400. }
  1401. }
  1402.  
  1403.  
  1404. if (action == 'slower') {
  1405.  
  1406. video.playbackRate -= value;
  1407. num = (video.playbackRate).toFixed(1);
  1408. keyboardEvent.currentSpeed = num;
  1409. layer.msg(num + " 倍");
  1410.  
  1411. } else if (action == 'faster') {
  1412.  
  1413. video.playbackRate += value;
  1414. num = (video.playbackRate).toFixed(1);
  1415. keyboardEvent.currentSpeed = num;
  1416. layer.msg(num + " 倍");
  1417.  
  1418. } else if (action == 'rewind') {
  1419.  
  1420. video.currentTime -= value;
  1421. layer.msg("- " + value + 's');
  1422. return;
  1423.  
  1424. } else if (action == 'advance') {
  1425.  
  1426. video.currentTime += value;
  1427. layer.msg("+ " + value + 's');
  1428. return;
  1429.  
  1430. } else if (action == 'reset') {
  1431.  
  1432. video.playbackRate = value;
  1433. num = (video.playbackRate).toFixed(1);
  1434. keyboardEvent.currentSpeed = num;
  1435. layer.msg(num + " 倍");
  1436.  
  1437. } else if (action == 'fast') {
  1438.  
  1439. video.playbackRate = value;
  1440. num = (video.playbackRate).toFixed(1);
  1441. keyboardEvent.currentSpeed = num;
  1442. layer.msg(num + " 倍");
  1443.  
  1444. } else if (action == 'keyMap') {
  1445.  
  1446. let i = layer.alert(
  1447. keyboardEvent.keyMapInfo, {
  1448. //icon: 1
  1449. anim: 2
  1450. },
  1451. function (index) {
  1452. //layer.msg('操作成功!');
  1453. layer.close(index);
  1454. });
  1455.  
  1456. layer.title('Key Map', i);
  1457. return;
  1458.  
  1459.  
  1460. } else if (action == 'playAll') {
  1461. $("#continuousPlayAll").trigger('click');
  1462. return;
  1463.  
  1464. } else if (action == 'stopPlayAll') {
  1465. $("#stopContinuousPlayAll").trigger('click');
  1466. return;
  1467.  
  1468. } else if (action == 'playPart') {
  1469. $("#continuousPlayPart").trigger('click');
  1470. return;
  1471.  
  1472. } else if (action == 'stopPlayPart') {
  1473. $("#stopContinuousPlayPart").trigger('click');
  1474. return;
  1475. } else if (action == 'onContinuousPlayFunc') {
  1476. onContinuousPlayFunc();
  1477. return;
  1478.  
  1479. } else if (action == 'offContinuousPlayFunc') {
  1480. offContinuousPlayFunc();
  1481. return;
  1482. } else if (action == 'showTips') {
  1483. showTips();
  1484. return;
  1485. }
  1486.  
  1487.  
  1488.  
  1489. }
  1490.  
  1491.  
  1492. /************************************
  1493. * tips module
  1494. */
  1495.  
  1496. let tipsConfig = {
  1497. params: {
  1498. tipsMore: true,
  1499. tips: 1,
  1500. time: 6000
  1501. },
  1502. };
  1503.  
  1504. function showTips() {
  1505. layer.tips('全部连播', '#continuousPlayAll', tipsConfig.params);
  1506. layer.tips('终止全部连播', '#stopContinuousPlayAll', tipsConfig.params);
  1507. layer.tips('正常连播', '#continuousPlayPart', tipsConfig.params);
  1508. layer.tips('终止正常连播', '#stopContinuousPlayPart', tipsConfig.params);
  1509. }
  1510.  
  1511. /**********************************
  1512. * statistics
  1513. */
  1514. var meta = '<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"/>';
  1515. $("head").prepend(meta);
  1516. const clickEventMap = new Map([
  1517. ['mode-click', 1],
  1518. ['mode-download', 2],
  1519. ['refresh', 3],
  1520. ['reset', 4],
  1521. ['choose', 5],
  1522. ['confirm', 7],
  1523. ['downloadSrc', 8],
  1524. ['download-res', 9],
  1525. ['forward', 10],
  1526. ['reverse', 11],
  1527. ['continuousPlayAll', 26],
  1528. ['stopContinuousPlayAll', 27],
  1529. ['continuousPlayPart', 28],
  1530. ['stopContinuousPlayPart', 29],
  1531. ]);
  1532. const keyboardEventMap = new Map([
  1533. ['keyMap', 12],
  1534. ['showTips', 13],
  1535. ['faster', 14],
  1536. ['slower', 15],
  1537. ['rewind', 16],
  1538. ['advance', 17],
  1539. ['fast', 18],
  1540. ['reset', 19],
  1541. ['onContinuousPlayFunc', 20],
  1542. ['offContinuousPlayFunc', 21],
  1543. ['playPart', 22],
  1544. ['stopPlayPart', 23],
  1545. ['playAll', 24],
  1546. ['stopPlayAll', 25]
  1547. ]);
  1548. // ?s 内的操作记为 1 次 有效记录
  1549. const specialKeyboardEventMap = new Map([
  1550. ['faster', 14],
  1551. ['slower', 15],
  1552. ['rewind', 16],
  1553. ['advance', 17],
  1554. ]);
  1555. let statConfig = {
  1556. recordURL: config.base + '/hits/saveOrUpdateUsePostWithoutCORS',
  1557. //? s 内记 1
  1558. specialKeysInterval: 5000
  1559. }
  1560. let record = (fcId) => {
  1561.  
  1562. let params = {
  1563. htFcId: fcId
  1564. }
  1565. axios({
  1566. method: 'POST',
  1567. url: statConfig.recordURL,
  1568. data: qs.stringify(params),
  1569. headers: {
  1570. 'Content-Type': 'application/x-www-form-urlencoded'
  1571. }
  1572. }).then((response) => {
  1573. //succ
  1574. console.log("succ")
  1575. }).catch((error) => {
  1576. //err
  1577. console.log("err")
  1578. });
  1579. }
  1580.  
  1581. window.addEventListener("click", (event) => {
  1582. let id = event.target.id;
  1583. if (clickEventMap.has(id)) {
  1584. record(clickEventMap.get(id))
  1585. }
  1586. });
  1587.  
  1588.  
  1589.  
  1590.  
  1591. /**
  1592. * MosoteachHelper CSS
  1593. */
  1594. const styleTag = `
  1595. <style>
  1596. .helper-btn{
  1597. border:1px solid #aaa;
  1598. border-radius:25px;
  1599. width:10%;
  1600. color:#fff;
  1601. font-weight:1000;
  1602. box-shadow:darkgrey 3px 3px 7px 2px;
  1603. cursor:pointer;
  1604. transition: .2s;
  1605. }
  1606. .helper-btn-a:hover{
  1607. // opacity: 0.6; //透明度
  1608. // background-color: #4d79ff !important;
  1609. background-color: rgba(0, 151, 179,1) !important;
  1610. box-shadow: darkgrey 2px 2px 5px 1px !important;
  1611. }
  1612. .helper-btn-b:hover{
  1613. background-color:rgba(204, 0, 0,1) !important;
  1614. }
  1615. .helper-btn:active{
  1616. background-color:#002b80 !important;
  1617. border:3px solid #eee !important;
  1618. box-shadow: darkgrey 1px 1px 2px 1px !important;
  1619. }
  1620. #refresh{
  1621. float:right;
  1622. background-color:rgba(204, 0, 0,0.6);
  1623. }
  1624. #reset{
  1625. float:right;
  1626. background-color:rgba(204, 0, 0,0.6);
  1627. }
  1628. #mode-click{ background:rgba(0, 151, 179,0.7);}
  1629. #mode-download{ background:rgba(0, 151, 179,0.7);}
  1630. #confirm{ background:rgba(0, 151, 179,0.7);}
  1631. #downloadSrc{ background:rgba(0, 151, 179,0.7);}
  1632. #choose{ background:rgba(204, 0, 0,0.6);}
  1633. //#refresh{ background:rgba(0, 151, 179,0.7);}
  1634.  
  1635. .content-center{
  1636. display: -webkit-box;
  1637. display: -moz-box;
  1638. display: -ms-flexbox;
  1639. display: -webkit-flex;
  1640. display: flex;
  1641. /*垂直居中*/
  1642. -webkit-box-align: center;
  1643. -moz-box-align: center;
  1644. -ms-flex-align: center;
  1645. -webkit-align-items: center;
  1646. justify-content: center;
  1647. align-items: center;
  1648. justify-content: center;
  1649. }
  1650.  
  1651. .video-btn{
  1652. color:white;
  1653. font-size:20px;
  1654. width:20%;
  1655. height: 34px;
  1656. }
  1657.  
  1658. #keyMapInfo{
  1659. width:300px;
  1660. }
  1661.  
  1662. .keyMap-head{
  1663. font-size: 16px;
  1664. font-weight: 700;
  1665. }
  1666.  
  1667. .keyMap-name{
  1668. width: 50%;
  1669. font-size: 14px;
  1670. font-weight: 600;
  1671. cursor: pointer;
  1672. }
  1673.  
  1674. .keyMap-value{
  1675. width: 50%;
  1676. font-size: 16px;
  1677. font-weight: 700;
  1678. color: orange;
  1679. }
  1680.  
  1681. </style>`;
  1682. $(styleTag).appendTo('head');
  1683.  
  1684. //为每个资源添加下载按钮
  1685. $(".res-row-open-enable").each(function () {
  1686. if ($(this).find(".download-res-button").length > 0) return; //如果已经存在下载按钮(例如mp3),则不再添加
  1687. $(this).find("ul").html('<li id="download-res" class="download-ress download-res-button">下载</li>' + $(this).find("ul").html());
  1688. // $(this).find("ul").html('<li id="forward">正序点击</li>' + $(this).find("ul").html());
  1689. // $(this).find("ul").html('<li id="reverse">倒序点击</li>' + $(this).find("ul").html());
  1690. });
  1691. //单个资源下载
  1692. $(document).on('click', '#download-res', function () {
  1693. var resHref = $(this).parents(".res-row-open-enable").attr('data-href');
  1694. window.open(resHref);
  1695. });
  1696.  
  1697. $('<div id="functionAreaTitle" style="padding:0 20px">\
  1698. <div class="clear20"></div>\
  1699. <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="100%" color=#0BD SIZE=4>\
  1700. <div class="clear10"></div>\
  1701. <div class="res-row-title">\
  1702. <span style="color: #0BD;font-weight:600; font-size:16px"> 功能区 </span>\
  1703. <span > Powered by </span>\
  1704. <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>\
  1705. <span style="color: orange;font-weight:500; font-size:14px"> 查看快捷键 shift + m </span>\
  1706. <i class="slidedown-button manual-order-hide-part icon-angle-down" data-sort="1001"></i>\
  1707. </div>\
  1708. </div>\
  1709. <div class="clear20"></div>\
  1710. <!-- helper area Start -->\
  1711. <div id="functionAreaContent" class="hide-div" data-status="N" data-sort="1001" style="display: none;">\
  1712. <div id="helper" style="padding:0 40px;">\
  1713. <div class="res-row-title" >\
  1714. <span class="res-group-name">当前模式: </span>\
  1715. <span id="modeName" style="color: #0BD;font-weight:600">未选择 </span>\
  1716. <span class="span-display" style="color: red"> | ( 选择模式后,请按照提示操作,否则会出错;“模拟点击/下载”执行完毕后需刷新页面,数据才会更新。)</span>\
  1717. <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="997"></i>\
  1718. </div>\
  1719. <div class="hide-div" data-status="N" data-sort="997" style="display: none;">\
  1720. <form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
  1721. <input id="mode-click" class="helper-btn helper-btn-a" type="button" value="模拟点击">\
  1722. <input id="mode-download" class="helper-btn helper-btn-a" type="button" value="批量下载">\
  1723. <input id="reset" class="helper-btn helper-btn-a helper-btn-b" type="button" value="重置">\
  1724. <input id="refresh" class="helper-btn helper-btn-a helper-btn-b" type="button" value="刷新页面">\
  1725. </form>\
  1726. </div>\
  1727. <div id="module-3">\
  1728. <div class="clear30"></div>\
  1729. <div class="res-row-title" >\
  1730. <span class="res-group-name" >已选栏号:</span>\
  1731. <span id="barID" style="color: #0BD;font-weight:600"> 全选 </span>\
  1732. <span class="span-display" style="color: #0BD" > | (范围: 最大值为资源栏总数 / 不填写 则视为全选)</span>\
  1733. <span class="span-display" style="color: red">(注意:资源栏号是从资源区里第一栏开始)</span>\
  1734. <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="1000"></i>\
  1735. </div>\
  1736. <div class="hide-div" data-status="N" data-sort="1000" style="display: none;">\
  1737. <form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
  1738. <input id="bar_index" placeholder="选择栏号 [ if (value < 1) --> 1 ; if (value > max) --> max ] 选择多栏语法: 3-2-4-6 " \
  1739. 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%">&nbsp\
  1740. <input id="choose" class="helper-btn helper-btn-a helper-btn-b" type="button" value="重置">\
  1741. </form>\
  1742. </div>\
  1743. </div>\
  1744. <div id="module-1">\
  1745. <div class="clear30"></div>\
  1746. <div class="res-row-title" >\
  1747. <span class="res-group-name" >模拟批量点击/下载</span>\
  1748. <span class="span-display" style="color: #0BD" >(范围:以资源总数值作为范围最大值)</span>\
  1749. <span class="span-display" style="color: red">( 点击对应按钮,将打开较多页面,请耐心等待其自动关闭。可在“控制台”里查看运行日志)</span>\
  1750. <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="998"></i>\
  1751. </div>\
  1752. <div class="hide-div" data-status="N" data-sort="998" style="display: none;">\
  1753. <form class="appendTxt res-row" style="padding:20px 20px 0px 20px ; !important">\
  1754. <input id="head" class="indexNum" placeholder="起始位置 [ if (value < 1) --> 1 ; if (value > max) --> max ]" style="border:1px solid #0BD; border-radius:8px;width:42%" >&nbsp\
  1755. <input id="tail" class="indexNum" placeholder="结束位置 [ if (value < 1) --> 1 ; if (value > max) --> max ]" style="border:1px solid #0BD; border-radius:8px;width:42%">&nbsp\
  1756. <input id="confirm" class="helper-btn helper-btn-a" type="button" value="模拟点击">\
  1757. <input id="downloadSrc" class="helper-btn helper-btn-a" type="button" value="批量下载">\
  1758. </form>\
  1759. </div>\
  1760. </div>\
  1761. <div id="module-2">\
  1762. <div class="clear30"></div>\
  1763. <div class="res-row-title" >\
  1764. <span class="res-group-name" >模拟全部点击(耗时较长)</span>\
  1765. <span class="span-display" style="color: #0BD" >(范围:所有资源)</span>\
  1766. <span class="span-display" style="color: red">( 点击后,将会自动打开较多页面,请耐心等待其自动关闭。可在“控制台(F12 -> console)”里查看运行日志)</span>\
  1767. <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="999"></i>\
  1768. </div>\
  1769. <div class="hide-div" data-status="N" data-sort="999" style="display: none;">\
  1770. <div class="res-row drag-res-row" style="height:37px !important">\
  1771. <div class="operation manual-order-hide-part" style="float:left;!important">\
  1772. <ul style="margin-top:0px;"><li id="reverse">倒序点击</li><li id="forward">正序点击</li>\
  1773. <div class="clear"></div>\
  1774. </ul>\
  1775. </div>\
  1776. </div>\
  1777. </div>\
  1778. </div>\
  1779. <div id="module-4">\
  1780. <div class="clear30"></div>\
  1781. <div class="res-row-title" >\
  1782. <span class="res-group-name" >功能测试模块 </span>\
  1783. <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>\
  1784. <i class="icon-angle-down slidedown-button manual-order-hide-part" data-sort="1002"></i>\
  1785. </div>\
  1786. <div class="hide-div" data-status="N" data-sort="1002" style="display: none;">\
  1787. <div class="res-row drag-res-row" style="height:37px !important">\
  1788. <div class="operation manual-order-hide-part" style="float:left;!important">\
  1789. <ul style="margin-top:0px;">\
  1790. <li id ="continuousPlayMode">视频连续播放控件(按钮在视频界面)</li>\
  1791. <li > 快捷键系统( shift + m )</li>\
  1792. <div class="clear"></div>\
  1793. </ul>\
  1794. </div>\
  1795. </div>\
  1796. </div>\
  1797. </div>\
  1798. </div>\
  1799. </div>\
  1800. <!-- helper area End -->\
  1801. <div id="sourceTitle" style="padding:0 20px">\
  1802. <div class="clear10"></div>\
  1803. <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=3)" width="100%" color=#0BD SIZE=4>\
  1804. <div class="clear10"></div>\
  1805. <div class="res-row-title">\
  1806. <span style="color: #0BD;font-weight:600; font-size:16px"> 资源区 </span>\
  1807. </div>\
  1808. </div>\
  1809. ').insertAfter("#res-view-way");
  1810. // 初始化
  1811. $("#module-1,#module-2").css("display", "none");
  1812. $("#confirm, #downloadSrc, #mode-click, #mode-download").css("display", "inline");
  1813. // change mode
  1814. $(document).on('click', '#mode-click', function () {
  1815. $("#module-1, #module-2").css("display", "block");
  1816. // 等价于
  1817. // document.getElementById("module-1").style.display="block";
  1818. // document.getElementById("module-2").style.display="block";
  1819. // document.getElementById('confirm').style.display = document.getElementById('confirm').style.display=="inline"?"inline":"none";
  1820. $("#downloadSrc, #mode-download").css("display", "none");
  1821. // $("#mode-click").css({"background-color":"#0BD","color":"#fff"});
  1822. $("#modeName").text("模拟点击");
  1823. if (browserType() == "Chrome") {
  1824. newTabAlert("onDownload", "chrome://settings/downloads", 'active', function () {
  1825. alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
  1826. "(识别到您使用的是Chrome浏览器)" + "\n\n" +
  1827. " 已自动为你打开浏览器【设置】页面" + "\n" +
  1828. " 【提醒】:如果没有结果可在搜索框中搜索【保存位置】" + "\n" +
  1829. " 【 打开 】 “下载前询问每个文件的保存位置” 右侧按钮");
  1830. });
  1831. } else {
  1832. alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
  1833. "(以下只是 chrome 浏览器操作步骤)" + "\n" +
  1834. " 1. 新建 Tab 页\n" + " -->\n" +
  1835. " 2. 地址栏输入: chrome://settings/?search=downloads\n" + " -->\n" +
  1836. " 3. 打开 “下载前询问每个文件的保存位置” 右侧按钮");
  1837. }
  1838. });
  1839. $(document).on('click', '#mode-download', function () {
  1840. document.getElementById("module-1").style.display = "block";
  1841. $("#module-2, #confirm, #mode-click").css("display", "none");
  1842. // $("#mode-download").css({"background-color":"#0BD","color":"#fff"});
  1843. $("#modeName").text("批量下载");
  1844. if (browserType() == "Chrome") {
  1845. newTabAlert("offDownload", "chrome://settings/downloads", 'active', function () {
  1846. alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
  1847. "(识别到您使用的是Chrome浏览器)" + "\n\n" +
  1848. " 已自动为你打开浏览器【设置】页面" + "\n" +
  1849. " 【提醒】:如果没有结果可在搜索框中搜索【保存位置】" + "\n" +
  1850. " 【 关闭 】 “下载前询问每个文件的保存位置” 右侧按钮")
  1851. });
  1852. } else {
  1853. alert("操作提醒:\n" + "务必操作,否则请不要向下执行任何操作!!!\n" + "\n" +
  1854. "(以下只是 chrome 浏览器操作步骤)" + "\n" +
  1855. " 1. 新建 Tab 页\n" + " -->\n" +
  1856. " 2. 地址栏输入:chrome://settings/?search=downloads\n" + " -->\n" +
  1857. " 3. 关闭 “下载前询问每个文件的保存位置” 右侧按钮");
  1858. }
  1859. });
  1860. $(document).on('click', '#reset', function () {
  1861. $("#module-1,#module-2").css("display", "none");
  1862. $("#confirm, #downloadSrc, #mode-click, #mode-download").css("display", "inline");
  1863. // $("#mode-download, #mode-click").css({"background-color":"#fff","color":"#000"});
  1864. $("#modeName").text("未选择");
  1865.  
  1866. });
  1867. // 刷新
  1868. $(document).on('click', '#refresh', function () {
  1869. location.reload()
  1870. })
  1871. //资源栏总数
  1872. var srcBarSum = 0;
  1873. // 给分栏添加 id 易于按栏操作
  1874. $(".res-row-box").each(function (i, e) {
  1875. $(this).attr('id', 'id_' + i);
  1876. srcBarSum = i + 1;
  1877. });
  1878. //存储所有被选择的资源栏 id
  1879. var chosenIDs = [];
  1880. $(document).on('click', '#choose', function () {
  1881. //获取点击时按钮值
  1882. var val = $("#choose").val();
  1883. //接受用户输入的id 字符串
  1884. let inputString = $("#bar_index").val().trim();
  1885. //inputString经过清洗后得到的有效资源栏编号
  1886. let idsArr = cleanData(inputString);
  1887.  
  1888. if (val == "确认选择") {
  1889. /**
  1890. * 用户修改要选择的资源栏点击确认后
  1891. * 根据有效资源栏编号生成对应资源栏id存入数组备用
  1892. * 并显示被选择的所有有效资源栏
  1893. */
  1894.  
  1895. //无输入内容,选择全部栏
  1896. if (idsArr.length == 0) {
  1897. chosenIDs.push(".res-row-box");
  1898. } else {
  1899. //有输入内容,转化成对应的id,放入数组备用
  1900. for (let id of idsArr) {
  1901. chosenIDs.push("#id_" + (id - 1));
  1902. }
  1903. }
  1904. //test
  1905. // console.log(idsArr);
  1906. //var barID = $("#bar_index").val();
  1907. let barID_str = idsArr.length == 0 ? "全选" : idsArr;
  1908. //var barID_str = (barID > 0 && barID < 21) ? barID : "全选";
  1909. alert("小可爱,你已将要操作的资源栏修改为: " + barID_str);
  1910. $("#barID").text(barID_str);
  1911. $("#choose").val("重置");
  1912. $("#choose").css('background-color', 'rgba(204, 0, 0,0.6)');
  1913.  
  1914. } else {
  1915. /**
  1916. * 用户重置资源栏输入框
  1917. * 置空输入框 和 存储的所有资源栏id
  1918. * 被选择的资源栏设为全选
  1919. */
  1920. $("#bar_index").val("");
  1921. chosenIDs = [];
  1922. $("#choose").val("确认选择");
  1923. $("#choose").click();
  1924.  
  1925. }
  1926.  
  1927. });
  1928.  
  1929. // reset bar_index
  1930. $('#bar_index').bind("input propertychange", function (event) {
  1931. $("#choose").val("确认选择");
  1932. $("#choose").css('background-color', 'rgba(0, 151, 179,0.7)');
  1933. });
  1934.  
  1935.  
  1936. /**
  1937. * Main body
  1938. *
  1939. */
  1940.  
  1941. /**
  1942. * 根据指定的所有资源栏id,进行模拟点击
  1943. */
  1944. $(document).on('click', '#confirm', function () {
  1945. batchForMoreSrcBars("模拟点击", chosenIDs)
  1946. });
  1947.  
  1948. /**
  1949. * 根据指定的所有资源栏id,进行批量下载
  1950. *
  1951. */
  1952. $(document).on('click', '#downloadSrc', function () {
  1953. batchForMoreSrcBars("批量下载", chosenIDs)
  1954. });
  1955.  
  1956. /**
  1957. * 模拟正序点击全部资源
  1958. *
  1959. */
  1960. $(document).on('click', '#forward', function () {
  1961. clickAll("true")
  1962. });
  1963.  
  1964. /**
  1965. * 模拟倒序点击全部资源
  1966. *
  1967. */
  1968. $(document).on('click', '#reverse', function () {
  1969. clickAll("false")
  1970. });
  1971.  
  1972. /**
  1973. * Play videos continuously
  1974. */
  1975. $(document).on('click', '#continuousPlayMode', () => {
  1976. continuousPlay()
  1977. })
  1978.  
  1979.  
  1980. });