Greasy Fork is available in English.

职教云|智慧职教助手[稳定版]

智慧职教自动刷课,点击进入课程首页自动开始(播放视频第一次手动静音),可结合浏览器倍数插件一起使用(最好别超过1.5倍速)

// ==UserScript==
// @name         职教云|智慧职教助手[稳定版]
// @namespace    http://tampermonkey.net/
// @version      1.3.7
// @description  智慧职教自动刷课,点击进入课程首页自动开始(播放视频第一次手动静音),可结合浏览器倍数插件一起使用(最好别超过1.5倍速)
// @author       maple
// @match        https://zjy2.icve.com.cn/study/process/process.html*
// @match        https://zjy2.icve.com.cn/common/directory/directory.html*
// @icon         https://zjy2.icve.com.cn/common/images/logo.png
// @grant        none
// ==/UserScript==
////////课程首页参数///////////
//需要排除内容的列表
let exclude_array = [
    '自己添加需要过滤的内容名称,按照脚本说明来', '例如:----->', '《单筋实例》微课录像.mp4', '《梁、板裂缝宽度验算》微课录像.mp4'
];

//未完成的模块数组
let undoneModule = [];

// 播放视频是否静音【不静音改为:let videoMute = false; 】
let videoMute = true;

// [ppt,图片,文档,点击下一张的时间间隔;默认0.2表] 如果一个重复观看某个内容,可以手动调节,范围:【 0.1 --- 1 】
let ppt间隔 = 0.2;

//结束变量
let stop;

//包含章节的tr列表长度
let trList_len;

//模块下所有内容的数量
let contentList_num;

//模块指针
let point = 0;

//是否下一个模块
let next = false;

//排除次数
let excludeNum = 0;

// 点击学习页面就不在执行循环【内部会有延迟,所以需要停止】
let continue_for = true;


///////学习页面参数///////////

//判断视频内容的次数
let judgeCount = 0;

//判断内容结束条件
let judgeStop;

// 停止异步提交请求
let put_ajax;

// 内容标题
let content_title;


//用于判断是否为视频的变量【其他页面也有】
let play;

//学习内容的类型
let contentType = "";

//学习是否结束
let study_end = false;

// ppt,图片,文档,点击间隔
let interval = 1000 * ppt间隔;

// 验证码参数
let Finder


////////////////////////////////////////////////【begin】////////////////////////////////////////////////////////////

//课程首页的网址
let url = 'https://zjy2.icve.com.cn/study/process/process.html?'

//判断是课程首页还是学习页面
if (document.URL.indexOf(url) !== -1) {
    //课程首页
    console.log("dir-begin");
    stop = setInterval(main, 2000);
} else {
    //学习页面开始
    console.log("study-begin")
    judgeStop = setInterval(studyMain, 2000);
}


///////////////////////////////////////////////课程首页//////////////////////////////////////////

// 请求限制
let count = 0;

//课程首页主函数
function main() {

    // 待提交的数据
    let data = {
        "token": getIdentity()
    }

    if (count===0){
        // 请求后端获取需要排除的数据
        get(data);
        count++;
    }

    //首页加载超时处理
    exeTimeout();


    try {
        //数组中有数据就不需要获取数组
        if (undoneModule.length === 0) {
            findUndoneModule();
        }

        console.log("当前模块索引:" + point)
        openModule(undoneModule[point]);
        openSection(undoneModule[point]);
        clickContent(undoneModule[point]);
    } catch (e) {
        console.log("错误信息:" + e);
        //stop = setInterval(main, 5000);
    }

    next_module();
}

//找到所有未完成模块
function findUndoneModule() {
    //包含所有模块的div(里面还有别的标签)
    let div = document.getElementById("process_container");
    //过滤之后的所有模块[列表]
    let moduleList = div.getElementsByClassName("moduleList");

    //遍历模块数组
    for (let i = 0; i < moduleList.length; i++) {
        //找到每个模块的进度div
        let schedule = moduleList[i].getElementsByTagName("div")[2];
        //进度不为100%就添加到未完成
        if (" 100% " !== schedule.innerHTML) {
            //添加未完成模块到数组
            undoneModule.push(moduleList[i]);
        }
    }
    console.log("所有模块数组:")
    console.log(undoneModule);

}

//展开模块
function openModule(m1) {
    //模块展开状态
    let status = m1.getElementsByClassName("topic_container")[0].style.display;

    //状态不可见,展开模块
    if ("block" !== status) {
        //点击模块的三角箭头,【展开模块目录】
        m1.getElementsByTagName("span")[1].click();
    }
}

//展开模块下的章节
function openSection(m) {
    //包含模块下所有章节的div
    let div = m.getElementsByClassName("topic_container")[0];
    //包含章节的tr列表
    let trList = div.getElementsByTagName("tr");

    //等待才能获取到tr列表的长度
    if (trList_len === undefined || trList_len === 0) {
        trList_len = trList.length;
    }
    console.log("trList长度:" + trList_len)    //为零就等待下一次获取【为2表示这个是空模块】

    //前面有3个无效tr,不是章节(从索引为3的开始取)
    for (let i = 2; i < trList.length; i++) {
        if (i % 2 === 1) {
            //这个里面所有的都是有效章节 每一个为trList[i]
            //未打开才展开【未展开是有这个元素】
            let openStatus = m.getElementsByClassName("am-icon sh-toc-expand  topic_condensation am-icon-caret-right")[0];
            if (openStatus !== undefined) {
                trList[i].click();          //展开模块中的每一个章节
            }
            //console.log(trList[i])
        }
    }

    //trList=2时表示这个模块为空【未获取到数据trList=0】
    if (trList_len === 2) {
        //开始下一个模块
        next = true;
    }


}


//点击章节中的内容(ppt,视频...)
function clickContent(m) {
    //包含所有内容的div [一个模块下所有的,不含不同章节的内容]
    let contentDiv = m.getElementsByClassName("sh-toc-list");
    if (contentList_num === undefined || contentList_num === 0) {
        contentList_num = contentDiv.length;
    } else {
        console.log("div长度::" + contentList_num);
        //clearInterval(stop)
    }
    for (let i = 0; i < contentList_num && continue_for === true; i++) {
        let contentList = contentDiv[i];    //模块中某一个章节的所有内容

        //console.log(contentList);         //如果输出的内容下没有子标签,那么这个章节中没有内容(直接下一个模块)

        //判断章节是否为空 [空返回true]
        if (isEmptySection(contentList) && point === 0) {
            next = true;
            continue;
        }

        //一个章节中的所有模块
        let contentS = contentList.getElementsByClassName("sh-res-h am-margin-top-xs");
        //console.log(contentS)

        // 点击内容之后就不在循环
        for (let j = 0; j < contentS.length && continue_for === true; j++) {
            //最小单位 内容
            let content = contentS[j];
            //console.log(content)

            //有a标签才是内容,不然有可能是文件夹
            if (content.getElementsByTagName("a").length !== 0) {

                //内容中的span标签,用来获取这个内容的进度
                let span = content.getElementsByTagName("span")[0];

                //学习进度不是100%就点击
                if (span.title !== "100%" && span.title !== "99%" && span.title !== undefined && span.title !== "") {
                    //console.log("未完成:")
                    //console.log("学习进度:"+span.title);
                    //console.log(content);

                    // 获取类型
                    let type = content.getElementsByTagName("span")[1].innerHTML;

                    //排除异常的内容(数组中的自定义)
                    excludeAndExecute(content, type);
                } else if (i === contentS.length - 1 && span.title === "100%") {
                    //模块中的所有内容都已完成 【只是模块进度还没刷新,打开了这个模块】
                    next = true;    //下一个模块

                }


            }
        }
    }


}

//判断一个章节是否为空
function isEmptySection(contentList) {
    //子节点个数(为1就是章节为空[有一个text子标签])
    let childNodeNum = contentList.childNodes.length;
    //console.log("子节点的个数:"+childNodeNum);
    return childNodeNum <= 1;
}


//排除异常的内容(并点击)
function excludeAndExecute(content, type) {
    //内容下包含[标题]的a标签(取出innerHtml就是title)
    let content_title = content.getElementsByTagName("a")[0].innerHTML;

    //包含在数组中的就排除
    if (exclude_array.indexOf(content_title) >= 0) {
        console.log("排除的内容title: " + content_title);
        //开始下一个模块
        if (excludeNum >= 10) {
            next = true;
        }
        excludeNum++;
        return;
    }


    // 待提交的数据
    let data = {
        "token": getIdentity(),
        "title": content_title,
        "type": type.trim(),
    }

    // 发送异步请求,提交内容名称,在后端计数
    put(data);


    console.log('------------')
    console.log("未完成内容:点击观看 " + content_title)
    console.log('------------')


    //不包含就点击,并结束下面的
    content.click();


    // 不继续循环
    continue_for = false;

    next = true;
    clearInterval(stop);
}


//如果上面结束了,开始下一个模块
function next_module() {
    if (next) {
        point++;
        //重置
        trList_len = undefined;
        contentList_num = undefined;
        next = false;
    }
    //停止
    if (point >= 30) {
        clearInterval(stop);
        //刷新浏览器
        location.reload();
    }
}

//课程首页页面的超时提示
function exeTimeout() {
    //弹窗
    let popup = document.getElementsByClassName("popBox")[0];
    //弹窗存在
    if (popup) {
        console.log("超时弹窗")
        //确定按钮
        let button = popup.getElementsByClassName("sgBtn ok")[0];
        button.click();
        //刷新浏览器
        location.reload();
    }
}

// 获取浏览器身份标识
function getIdentity() {

    function bin2hex(str) {
        let result = "";
        for (i = 0; i < str.length; i++) {
            result += int16_to_hex(str.charCodeAt(i));
        }
        return result;
    }

    function int16_to_hex(i) {
        let result = i.toString(16);
        let j = 0;
        while (j + result.length < 4) {
            result = "0" + result;
            j++;
        }
        return result;
    }

    let canvas = document.createElement('canvas');
    let ctx = canvas.getContext('2d');
    let txt = 'http://security.tencent.com/';
    ctx.textBaseline = "top";
    ctx.font = "14px 'Arial'";
    ctx.fillStyle = "#f60";
    ctx.fillRect(125, 1, 62, 20);
    ctx.fillStyle = "#069";
    ctx.fillText(txt, 2, 15);
    ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
    ctx.fillText(txt, 4, 17);

    let b64 = canvas.toDataURL().replace("data:image/png;base64,", "");
    let bin = atob(b64);

    return bin2hex(bin.slice(-16, -12));
}

///////////////////////////////////////////////学习页面///////////////////////////////////////////////

//学习页面主函数
function studyMain() {
    //处理上一次学习记录弹窗
    hint();
    //判断学习内容类型
    judgeType();
    //控制器【不同类型执行不同操作】
    controller();
}

//处理提示[上次学习与这次学习]
function hint() {
    let link = document.getElementsByClassName("link");
    if (link.length === 0) {
        //没有弹出提示
        return;
    }
    //不会在控制台显示,因为点击之后会刷新浏览器
    console.log("上一次学习记录【已处理】");

    //【弹出提示】点击本次的学习记录
    link[1].getElementsByTagName("a")[0].click();
}

//判断当前学习的内容类型 【都需要第二次才能获取到】
function judgeType() {

    //执行一次判断【自增】
    judgeCount++;

    //文档   【正常】
    let doc = document.getElementsByClassName("swiper-pagination-total")[0];
    if (doc) {
        // console.log("文档")
        // console.log(doc);
        contentType = "文档";
    }

    //ppt[动态]  【正常】
    let ppt = document.getElementsByClassName("stage-next-btn")[0];
    if (ppt) {
        // console.log("ppt[动态]")
        // console.log(ppt);
        contentType = "ppt[动态]";
    }

    //ppt[静态] 【正常】
    let pt = document.getElementsByClassName("MPreview-pageCount")[0];
    if (pt) {
        //获取到之后输出
        // console.log("ppt[静态]");
        // console.log(pt)
        contentType = "ppt[静态]";
    }

    // 图片
    let image = $("#docPlay").children("img")[0];
    if (image) {
        // console.log("[图片]")
        // console.log(image)
        contentType = "图片";
    }

    // 资源
    let src = document.getElementsByClassName("np-link-go-bd");
    if (src.length === 1) {
        contentType = "资源";
    }


    //视频【这个其他页面也有,主要是用来获取后面的节点】  【正常】
    if (!play) {
        play = document.getElementById("docPlay");
    }
    //下面有内容才是继续
    if (play.innerHTML !== "") {
        //play中有这个类的标签就是视频
        let set = play.getElementsByClassName("jw-media jw-reset")[0];
        if (set) {
            // console.log("视频")
            // console.log(set);
            contentType = "视频";
        }
    }

    //判断学习内容【只判断3次】
    if (judgeCount >= 3) {
        clearInterval(judgeStop)
    }
}


//控制器
function controller() {
    if (contentType !== "") {
        //输出学习内容类型并结束获取
        console.log(contentType);
        clearInterval(judgeStop);

        //相应的内容做对应的操作
        sWitch(contentType);
        return;
    }

    //3次之后还未获取到 [重复获取]
    if (contentType === "" && judgeCount === 3) {
        console.log("未获取到学习内容类型");
        judgeStop = setInterval(studyMain, 4000);

    }
}


//执行相应的处理操作
function sWitch(type) {
    switch (type) {
        case      "文档":
            exeDoc();
            break;
        case "ppt[动态]":
            exePpt();
            break;
        case "ppt[静态]":
            exePt();
            break;
        case      "视频":
            exeVideo();
            break;
        case      "图片":
            exeOther();
            break;
        case      "资源":
            exeOther();
            break;
    }
}


// 处理图片
function exeOther() {
    // 2秒之后直接返回首页
    study_end = true;
    setTimeout(back, 1000);
}


//处理文档
function exeDoc() {
    //当前图片页数
    let current = document.getElementsByClassName("swiper-pagination-current")[0].innerHTML;

    //所有图片页数
    let all = document.getElementsByClassName("swiper-pagination-total")[0].innerHTML;

    //点击次数【可能存在延迟,多点击2次】
    let count = all - current + 3;

    console.log("点击次数:" + count);
    let docStop = setInterval(loop, interval);


    function loop() {
        // 学习异常处理
        except();

        if (count !== 0) {
            //下一张按钮 [可能需要等待]
            let button = document.getElementsByClassName("swiper-button-next")[0];
            button.click();
            count--;
        } else {
            //为零[点击完毕退出]
            clearInterval(docStop);
            //内容学习完毕
            study_end = true;
            console.log("结束");
            //回到首页
            back();
        }
    }


}

//处理ppt[动态]
function exePpt() {

    //新页的数值
    let new_pag = "xxx";
    //结束条件
    let pptStop = setInterval(loop, interval);
    //一张ppt的动态计数 【最大值20】
    let count = 0;

    function loop() {
        // 学习异常处理
        except();
        //新页面的数值还在变化
        if (new_pag !== getNum()) {
            //上一次和这一次的页数不一致【点击下一张,并】
            new_pag = getNum();
            //图片下一张按钮[点击]
            let button = document.getElementsByClassName("stage-next-btn")[0];
            button.click();
            console.log("静态点击");

            //如果有了新的ppt页执行,动态计数清零
            count = 0;
        } else {

            console.log("动态计数:" + count)
            count++;
            //如果指定次数还没有新页,则认定结束
            if (count === 50) {
                clearInterval(pptStop);
                console.log("结束");
                //内容学习完毕
                study_end = true;
                //回到首页
                back();
                //结束
                return;
            }

            //稍微延时之后点击一个ppt图片中的动态效果
            sleep(50).then(() => {
                //先点击下一张
                let button = document.getElementsByClassName("stage-next-btn")[0];
                button.click();

                //没有变化,可能是到了动态的ppt,也有可能是结束了
                loop();
            })
        }

    }

    function getNum() {
        //新的ppt数量【随点击而变化,不变化了就说明没有新的图片了,结束了】
        new_pag = $("input[name='newlyPicNum']").val();
        return new_pag;
    }

}

//处理ppt[静态]
function exePt() {
    let new_pag_num = 'xxx';
    let ptStop = setInterval(loop, interval);

    function loop() {
        // 学习异常处理
        except();
        //页数还在变化执行
        if (new_pag_num !== getNum()) {
            //重新获取
            new_pag_num = getNum();
            //下一张按钮 [这是个侧边按钮(如果是文档走了这里,就没有侧边按钮)]
            //let button = document.getElementsByClassName("MPreview-arrowBottom")[0];

            // 下一张按钮,下方按钮【类似ppt的文档也能使用】
            let button = document.getElementsByClassName("MPreview-pageNext current")[0];
            if (button) {
                button.click();
            }


        } else {
            //这一次和上一次相同了,结束
            clearInterval(ptStop);
            //内容学习完毕
            study_end = true;
            //回到首页
            back();
            console.log("结束");
        }
    }

    function getNum() {
        //新的ppt数量【随点击而变化,不变化了就说明没有新的图片了,结束了】
        new_pag_num = $("input[name='newlyPicNum']").val();
        return new_pag_num;
    }


}

//处理视频
function exeVideo() {
    //视频进度
    let schedule = "xx";
    //视频停止计数
    let count = 0;
    // 定义
    let player;
    init();
    let videoStop = setInterval(loop, 2000);

    function init() {
        // 定位 JwPlayer播放器
        player = top.jwplayer(document.getElementsByClassName("jwplayer")[0].id);

        // 设置静音
        player.setMute(videoMute);
    }

    function loop() {
        // 播放视频
        play();
        // 学习异常处理
        except();
        //视频进度到达100%就退出
        if (schedule === "100%") {
            //结束
            console.log("结束")
            clearInterval(videoStop);
            //内容学习完毕
            study_end = true;
            //回到首页
            back();
            return;
        } else if (schedule === getSchedule()) {
            //当前进进度和上一次的进度是否一样【播放】
            player.play();
            if (count >= 10) {
                //重复10次进度一直,刷新浏览器
                location.reload();
            }
            //视频停止计数
            count++;
        } else {
            //没有停止【计数清零】
            count = 0;
        }
        //获取进度
        schedule = getSchedule();
        console.log("视频进度:" + getSchedule());
    }

    // 播放视频
    function play() {

        // 播放
        if (player.getState() !== "playing") {
            if (player.getState() !== "complete"){
                player.play();
            }
        }
    }

}

//获取视频进度
function getSchedule() {
    try {
        let bar = document.querySelector(".jw-progress");
        return bar.style.width;
    } catch (e) {
        return "error";
    }
}

//学习异常弹窗
function except() {
    //找到异常按钮a标签
    let a = document.getElementsByClassName("sgBtn ok")[0];
    //异常按钮存在就点击
    if (a !== undefined && a !== null) {
        console.log("学习异常【已处理】")
        console.log(a);
        a.click();
    } else {
        //console.log("无异常")
    }
}


//学习完毕,返回首页
function back() {
    //是否学习结束
    if (study_end) {
        //返回按钮
        let back = document.getElementsByClassName("np-menu-nav-li")[0];
        //延迟5秒之后返回首页
        sleep(9000).then(() => {
            back.getElementsByTagName("a")[0].click();
        })
    }
}


//延时函数
function sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}

// 异步请求提交
function put(data){
    put_ajax = $.ajax({
        url: 'https://121.196.195.71/cloud/add',
        type: 'put',
        data: data,
        success: function(resp){
            if (resp.code === 200) {
                console.log("提交msg:" + resp.msg);
            }
        },
        error: function(e) {
            console.log("提交服务器数据错误")
        }
    });
}


function get(data){
    $.ajax({
        url: 'https://121.196.195.71/cloud/get',
        type: 'post',
        data: data,
        success: function(resp){
            if (resp.code === 200) {
                addData(resp.data);
            }
        },
        error: function(e) {
            console.log("获取服务器数据错误: ")
            console.log(e)
        }
    });
}

function addData(data){
    console.log("待添加排除课程:");
    console.log(data)
    console.log("----------------")

    // 合并本地和云端排除内容
    exclude_array.push(...data);
    console.log("所有排除内容:")
    console.log(exclude_array)

    // 以及加入排除列表不在添加[这种情况是get请求还没有响应的时候]
    if (exclude_array.indexOf(content_title) >= 0){
        // 取消添加的请求
        put_ajax.abort();
    }
}