// ==UserScript== // @name UOOC assistant // @name:en UOOC assistant // @namespace http://tampermonkey.net/ // @version 0.9.0 // @description 【使用前先看介绍/有问题可反馈】【欢迎一键三连(好评+打赏+收藏),你的支持是作者维护下去的最大动力!】UOOC优课联盟助手(UOOC assistant):视频自动连播,可选自动二倍速播放(因为超过二倍速可能无法记录任务点),可选是否静音,可选是否播放,离开页面能够继续保持状态,自动回答视频中途弹出问题;键盘左右方向键可控制快进快退;自动签到慕课;如果视频标题下面出现 倍速/静音/播放 选项说明脚本正常启动运行。 // @description:en 【使用前先看介绍/有问题可反馈】【欢迎一键三连(好评+打赏+收藏),你的支持是作者维护下去的最大动力!】UOOC优课联盟助手(UOOC assistant):视频自动连播,可选自动二倍速播放(因为超过二倍速可能无法记录任务点),可选是否静音,可选是否播放,离开页面能够继续保持状态,自动回答视频中途弹出问题;键盘左右方向键可控制快进快退;自动签到慕课;如果视频标题下面出现 倍速/静音/播放 选项说明脚本正常启动运行。 // @author cc // @include http://www.uooc.net.cn/* // @grant none // @downloadURL none // ==/UserScript== (function () { 'use strict'; const jsName = 'UOOC-assistant.js'; const RECURSION_DURATION = 500; if (location.host == 'www.uooc.net.cn') { console.log(`excute ${jsName}`); let recursive = () => { let extraTime = 0; try { let done = false; let video = document.querySelector('#player_html5_api'); if (video) { if (document.getElementById('rate').checked) video.playbackRate = 2; else video.playbackRate = 1; if (document.getElementById('volume').checked) video.muted = true; else video.muted = false; if (document.getElementById('play').checked && !video.ended) video.play(); else video.pause(); if (video.ended) { done = true; }; let quizLayer = document.querySelector('#quizLayer'); if (quizLayer && quizLayer.style.display != 'none') { if (done) { setTimeout(() => { document.querySelectorAll('.layui-layer-shade').forEach(e => e.style.display = 'none'); }, RECURSION_DURATION << 1); }; let source = JSON.parse(document.querySelector('div[uooc-video]').getAttribute('source')); let quizList = source.quiz; let quizIndex = 0; let quizQuestion = document.querySelector('.smallTest-view .ti-q-c').innerHTML; for (let i = 0; i < quizList.length; i++) { if (quizList[i].question == quizQuestion) { quizIndex = i; break; }; }; let quizAnswer = eval(quizList[quizIndex].answer); let quizOptions = quizLayer.querySelector('div.ti-alist'); for (let ans of quizAnswer) { let labelIndex = ans.charCodeAt() - 'A'.charCodeAt(); quizOptions.children[labelIndex].click(); }; // end for quizLayer.querySelector('button').click(); extraTime = 1000; }; // end if if (!done) { if (video.paused && document.getElementById('play').checked) { video.play(); } else { document.querySelectorAll('.layui-layer-shade, #quizLayer').forEach(e => e.style.display = 'none'); }; }; }; // end if (video) if (!done) { setTimeout(recursive, RECURSION_DURATION + extraTime); } else if (video) { let current_video = document.querySelector('.basic.active'); let next_part = current_video.parentNode; let next_video = current_video; // 定义判断是否视频的函数 let isVideo = node => Boolean(node.querySelector('span.icon-video')); // 定义是否可返回上一级目录的函数 let canBack = () => { return Boolean(next_part.parentNode.parentNode.tagName == 'LI'); }; // 定义更新至后续视频的函数 let toNextVideo = () => { next_video = next_video.nextElementSibling; while (next_video && !isVideo(next_video)) { next_video = next_video.nextElementSibling; }; }; // 定义判断是否存在视频的函数 let isExistsVideo = () => { let _video = next_part.firstElementChild; while (_video && !isVideo(_video)) { _video = _video.nextElementSibling; }; return Boolean(_video && isVideo(_video)); }; // 定义判断是否存在后续视频的函数 let isExistsNextVideo = () => { let _video = current_video.nextElementSibling; while (_video && !isVideo(_video)) { _video = _video.nextElementSibling; }; return Boolean(_video && isVideo(_video)); }; // 定义检查文件后是否存在后续目录的函数 let isExistsNextListAfterFile = () => { let part = current_video.parentNode.nextElementSibling; return Boolean(part && part.childElementCount > 0); }; // 定义更新文件后的后续目录的函数 let toNextListAfterFile = () => { next_part = next_part.nextElementSibling; }; // 定义返回上一级的函数 let toOuterList = () => { next_part = next_part.parentNode.parentNode; }; // 定义返回主条目的函数 let toOuterItem = () => { next_part = next_part.parentNode; }; // 定义检查列表后是否存在后续目录的函数 let isExistsNextListAfterList = () => { return Boolean(next_part.nextElementSibling); }; // 定义进入列表后的后续目录的函数 let toNextListAfterList = () => { next_part = next_part.nextElementSibling; }; // 定义展开目录的函数 let expandList = () => { next_part.firstElementChild.click(); }; // 定义进入展开目录的第一个块级元素的函数 let toExpandListFirstElement = () => { next_part = next_part.firstElementChild.nextElementSibling; if (next_part.classList.contains('unfoldInfo')) { next_part = next_part.nextElementSibling; }; }; // 定义判断块级元素是否目录列表的函数 let isList = () => { return Boolean(next_part.tagName == 'UL'); }; // 定义目录列表的第一个目录的函数 let toInnerList = () => { next_part = next_part.firstElementChild; }; // 定义进入文件列表的第一个视频的函数 let toFirstVideo = () => { next_video = next_part.firstElementChild; while (next_video && !isVideo(next_video)) { next_video = next_video.nextElementSibling; }; }; // 定义模式 let mode = { FIRST_VIDEO: 'FIRST_VIDEO', NEXT_VIDEO: 'NEXT_VIDEO', LAST_LIST: 'LAST_LIST', NEXT_LIST: 'NEXT_LIST', INNER_LIST: 'INNER_LIST', OUTER_LIST: 'OUTER_LIST', OUTER_ITEM: 'OUTER_ITEM', } // 定义搜索函数 let search = (_mode) => { switch (_mode) { case mode.FIRST_VIDEO: // mode = 0 if (isExistsVideo()) { toFirstVideo(); next_video.click(); setTimeout(recursive, RECURSION_DURATION); } else { // perhaps there is an exam, end recursion }; break; case mode.NEXT_VIDEO: // mode == 1 if (isExistsNextVideo()) { toNextVideo(); next_video.click(); setTimeout(recursive, RECURSION_DURATION); } else if (isExistsNextListAfterFile()) { search(mode.LAST_LIST); } else { search(mode.OUTER_ITEM); }; break; case mode.LAST_LIST: // mode == 2 toNextListAfterFile(); toInnerList(); search(mode.INNER_LIST); break; case mode.NEXT_LIST: // mode == 3 toNextListAfterList(); search(mode.INNER_LIST); break; case mode.INNER_LIST: // mode == 4 expandList(); (function waitForExpand () { if (next_part.firstElementChild.nextElementSibling) { toExpandListFirstElement(); if (isList()) { toInnerList(); search(mode.INNER_LIST); } else { search(mode.FIRST_VIDEO); }; } else { setTimeout(waitForExpand, RECURSION_DURATION); }; })(); break; case mode.OUTER_LIST: // mode == 5 toOuterList(); if (isExistsNextListAfterList()) { search(mode.NEXT_LIST); } else if (canBack()) { search(mode.OUTER_LIST); } else { // perhaps there is no next list }; break; case mode.OUTER_ITEM: // mode == 6 toOuterItem(); if (isExistsNextListAfterList()) { toNextListAfterList(); search(mode.INNER_LIST); } else if (canBack()){ search(mode.OUTER_LIST); } else { // perhaps there is no list }; break; default: break; }; }; try { search(mode.NEXT_VIDEO); } catch (e) { console.log(e); }; }; } catch (e) { console.log(e); }; }; // end recursive let wait = () => { if (document.readyState == 'complete') { console.log('ready to set checkboxes.'); let getCheckbox = (name, text) => { let p = document.createElement('p'); p.style.color = '#cccccc'; let checkbox = document.createElement('input'); checkbox.id = name; checkbox.type = 'checkbox'; checkbox.checked = true; checkbox.name = name; checkbox.value = name; checkbox.style.marginLeft = '25px'; p.append(checkbox); let label = document.createElement('label'); label.setAttribute('for', name); label.innerText = text; label.style.marginLeft = '15px'; p.append(label); p.style.margin = '5px'; return p; }; let rateCheckbox = getCheckbox('rate', '倍速'); let volumeCheckbox = getCheckbox('volume', '静音'); let playCheckbox = getCheckbox('play', '播放'); let head = document.querySelector('.learn-head'); let container = document.createElement('div'); container.id = 'container'; container.style.display = 'flex'; container.style.flexDirection = 'row'; container.style.alignItems = 'center'; container.append(rateCheckbox); container.append(volumeCheckbox); container.append(playCheckbox); let a = document.createElement('a'); a.href = 'https://s1.ax1x.com/2020/10/23/BEnDsS.png'; a.target = '_blank'; a.innerHTML = '本脚本使用完全免费,您的打赏是作者维护下去的最大动力!点此打赏作者😊'; a.style.color = '#cccccc'; a.style.fontWeight = 'bold'; a.style.height = 'min-height'; a.style.margin = '0px 20px 0px'; a.style.padding = '2px 5px 2px'; a.style.bordRadius = '5px'; a.style.fontSize = '13px'; container.appendChild(a); head.append(container); console.log('checkboxes have been set.'); let cid = location.href.match(/\d+/g)[0]; let httpRequest = new XMLHttpRequest(); httpRequest.open('GET', `http://www.uooc.net.cn/home/course/info?cid=${cid}`, true); httpRequest.send(); httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { console.log('Automatic sign-in succeeded.'); }; }; document.onkeydown = (event) => { let k = event.key; let complete = false; let div = document.querySelector('div.basic.active'); if (div && div.classList.contains('complete')) complete = true; let video = document.getElementById('player_html5_api'); if (video) { switch (k) { case 'ArrowLeft': video.currentTime -= 10; break; case 'ArrowRight': if (complete) video.currentTime += 10; break; }; }; }; recursive(); } else { setTimeout(wait, RECURSION_DURATION); }; }; // end wait wait(); } })();