UOOC assistant

【使用前先看介绍/有问题可反馈】【欢迎一键三连(好评+打赏+收藏),你的支持是作者维护下去的最大动力!】UOOC优课联盟助手(UOOC assistant):视频自动连播,可选自动二倍速播放(因为超过二倍速可能无法记录任务点),可选是否静音,可选是否播放,离开页面能够继续保持状态,自动回答视频中途弹出问题;键盘左右方向键可控制快进快退;自动签到慕课;如果视频标题下面出现 倍速/静音/播放 选项说明脚本正常启动运行。

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

// ==UserScript==
// @name         UOOC assistant
// @name:en      UOOC assistant
// @namespace    http://tampermonkey.net/
// @version      0.9.1
// @description  【使用前先看介绍/有问题可反馈】【欢迎一键三连(好评+打赏+收藏),你的支持是作者维护下去的最大动力!】UOOC优课联盟助手(UOOC assistant):视频自动连播,可选自动二倍速播放(因为超过二倍速可能无法记录任务点),可选是否静音,可选是否播放,离开页面能够继续保持状态,自动回答视频中途弹出问题;键盘左右方向键可控制快进快退;自动签到慕课;如果视频标题下面出现 倍速/静音/播放 选项说明脚本正常启动运行。
// @description:en  【使用前先看介绍/有问题可反馈】【欢迎一键三连(好评+打赏+收藏),你的支持是作者维护下去的最大动力!】UOOC优课联盟助手(UOOC assistant):视频自动连播,可选自动二倍速播放(因为超过二倍速可能无法记录任务点),可选是否静音,可选是否播放,离开页面能够继续保持状态,自动回答视频中途弹出问题;键盘左右方向键可控制快进快退;自动签到慕课;如果视频标题下面出现 倍速/静音/播放 选项说明脚本正常启动运行。
// @author       cc
// @include      http://www.uooc.net.cn/*
// @grant        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/11/08/BTeRqe.png';
                a.target = '_blank';
                a.innerHTML = '<u>本脚本使用完全免费,您的打赏是作者维护下去的最大动力!点此打赏作者😊</u>';
                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();
    }
})();