Greasy Fork

Greasy Fork is available in English.

UOOC assistant

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

当前为 2020-11-09 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         UOOC assistant
// @name:en      UOOC assistant
// @namespace    http://tampermonkey.net/
// @version      0.9.3
// @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) {
                    if (!document.getElementById('continue').checked) {
                        video.currentTime = 0;
                        // video.ended = false;
                        setTimeout(recursive, RECURSION_DURATION + extraTime);
                    } else {
                        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 continueCheckbox = getCheckbox('continue', '连播');
                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);
                container.append(continueCheckbox);
                let div = document.createElement('div');
                div.innerHTML = '( 提示:键盘的 \u21FD 和 \u21FE 可以控制快进/快退,空格键可以控制播放/暂停 )';
                div.style.color = '#cccccc';
                div.style.fontWeight = 'bold';
                div.style.height = 'min-height';
                div.style.margin = '0px 20px 0px';
                div.style.padding = '2px 5px 2px';
                div.style.bordRadius = '5px';
                div.style.fontSize = '13px';
                container.appendChild(div);
                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;
                            case ' ':
                                let continueCheckbox = document.getElementById('play');
                                continueCheckbox.checked = !continueCheckbox.checked;
                                break;
                        };
                    };
                };
                recursive();
            } else {
                setTimeout(wait, RECURSION_DURATION);
            };
        }; // end wait
        wait();
    }
})();