云班课高效助手

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

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

QingJ © 2025

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