吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5234|回复: 9
收起左侧

[其他原创] SCNU长江雨课堂网课脚本

  [复制链接]
leqaq 发表于 2022-11-5 23:35
本帖最后由 leqaq 于 2022-11-6 11:26 编辑

我的第一个油猴脚本

Tampermonkey 中文名俗称油猴,是一款免费的浏览器插件,目前最为流行的用户脚本管理器,用户可以通过油猴添加和使用脚本,而脚本是一种可以修改网页 JavaScript 的程序。通过这些脚本,可以实现视频解析、音乐下载、网盘直连解析、豆瓣资源下载、百度文库增强、屏蔽网站广告等功能。总之,通过油猴,我们可以实现很多想象不到的强大功能,而这些功能的背后就是依托庞大的脚本市场,如 greasyfork:https://greasyfork.org/zh-CN,我编写的脚本也是发布在这上面

1、契机

从我接触到浏览器插件就用上了油猴,也算是打开了新世界的大门一样,接触了各式各样的脚本,比如:网页 vip 视频解析、百度网盘直链下载、百度文库复制、csdn 免登录复制、网页去广告、刷网课脚本等等,给我的上网冲浪生活带来了很多的便利,greasyfork 上的脚本基本上都能满足我的需求,而最近学校开了网课,网课每次看完之后就暂停了,要手动点下一节,这就产生了需求,而恰好 greasyfork 上没有这样的脚本,于是萌生了自己实现这样一个脚本的想法。

2、初步构思

  1. 在课程列表界面,自动检测没有完成的课程,点进去观看
  2. 课程视频自动播放且静音,开启视频倍速播放
  3. 检测视频的完成度,当完成度大于 95 则跳转至下一单元
  4. 直到所有课程都观看完毕

3、技术

需求实现实质就是一些逻辑判断,获取到指定的元素的值,然后做 if 判断以及页面跳转

  1. 原生 js 的 document 对象,获取指定的 dom 元素
  2. jQuery 方便获取 dom 节点
  3. 油猴提供的 API,GM_getValue 根据 key 获取 value、GM_setValue 设置 key-value 对、unsafeWindow 是油猴提供的沙盒环境,在 unsafeWindow 环境下,可以使用油猴提供的强大函数

4、脚本开发

在油猴插件的脚本管理界面创建新的脚本即可。我是用在 vscode 中写 js 代码,再复制到油猴中保存,然后在浏览器调试。这样挺不方便的,也没有去研究更高效的办法。

因为我对 js 也只是一知半解,再加上也没有那么多时间去研究,只以实现功能为准,没有去追求代码的优雅。

代码写的比较暴力,用了很多计时器。

5、脚本代码

// ==UserScript==
// home.php?mod=space&uid=170990         scnu华南师范大学网课脚本
// home.php?mod=space&uid=467642    http://tampermonkey.net/
// home.php?mod=space&uid=1248337      1.2
// @description  scnu 华南师范大学 长江雨课堂 网课自动化脚本
// home.php?mod=space&uid=686208       hqzqaq
// home.php?mod=space&uid=593100         https://statics.scnu.edu.cn/statics/images/favicon.ico
// home.php?mod=space&uid=609072        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// home.php?mod=space&uid=195849        https://scnuyjs.yuketang.cn/pro/*
// @run-at       document-end
// @license      MIT
// @require https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js
// ==/UserScript==

(function () {
    "use strict";
    // 多长时间刷新一下页面,单位 分钟
    const reloadTime = 10;
    // 视频播放速率,可选值 [1,1.25,1.5,2],默认为二倍速
    const rate = 2;

    window.onload = function () {
        // 网课页面跳转
        function getElTooltipItemList() {
            return document.getElementsByClassName("el-tooltip leaf-detail");
        }

        function getElTooltipList() {
            return document.getElementsByClassName("el-tooltip f12 item");
        }

        // 静音
        function claim() {
            $(
                "#video-box > div > xt-wrap > xt-controls > xt-inner > xt-volumebutton > xt-icon"
            ).click();
        }

        function fun(className, selector)
        {
            var mousemove = document.createEvent("MouseEvent");
            mousemove.initMouseEvent("mousemove", true, true, unsafeWindow, 0, 10, 10, 10, 10, 0, 0, 0, 0, 0, null);
            document.getElementsByClassName(className)[0].dispatchEvent(mousemove);
            document.querySelector(selector).click();
        }

        // 加速
        function speed() {
            let keyt = '';
            if(rate === 2 || rate === 1){
                keyt = "[keyt='" + rate + ".00']"
            }else{
                keyt = "[keyt='" + rate + "']"
            }
            fun("xt_video_player_speed", keyt);
        }

        const getElementInterval = setInterval(function () {
            const elTooltipList = getElTooltipList();
            const elTooltipItemList = getElTooltipItemList();
            if (elTooltipList) {
                for (let index = 0; index < elTooltipList.length; index++) {
                    const element = elTooltipList[index];
                    const textContent = element.textContent;
                    //const textContent = ''
                    if (textContent === "未开始" || textContent === "未读") {
                        // 判断是否是习题
                        if(elTooltipItemList[index].innerText.indexOf('习题')!= -1){
                            continue;
                        }
                        // 判断是否已过学习时间
                        if (elTooltipItemList[index].children[1].children[0].innerText.indexOf("已过") != -1) {
                            continue;
                        }
                        window.clearInterval(getElementInterval);
                        GM_setValue("rowUrl", window.location.href.toString());
                        // 网课页面跳转
                        elTooltipItemList[index].click();
                        window.close();
                        break;
                    }
                }
            }
        }, 1000);

        let video;
        const videoPlay = setInterval(function () {
            // 获取播放器
            video = document.getElementsByClassName("xt_video_player")[0];
            if (!video) {
                return;
            }
            setTimeout(function () {
                // 视频开始5s之后再开启倍速
                speed()
            },5000);
            claim();
            window.clearInterval(videoPlay);
        }, 500);

        // 是否播放完成的检测
        const playTimeOut = setInterval(function () {
            if (!video) {
                return;
            }
            video.play();

            // 没有静音
            if (video.volume != 0) {
                claim();
            }
            const completeness = $(
                "#app > div.app-wrapper > div.wrap > div.viewContainer.heightAbsolutely > div > div.video-wrap > div > div > section.title > div.title-fr > div > div > span"
            );
            if (!completeness) {
                return;
            }
            if (typeof completeness[0] == "undefined") {
                return;
            }
            const videoText = completeness[0].innerHTML
            if (videoText) {
                let str = videoText.toString();
                const succ = str.substring(4, str.length - 1);
                const succNum = parseInt(succ);
                if (succ >= 95) {
                    const url = GM_getValue("rowUrl");
                    if(url){
                        window.clearInterval(playTimeOut);
                        window.location.replace(url);
                    }
                }
            }

        }, 1000);

        // 是否为阅读类型
        const readInterval = setInterval(function () {
            const read = $(
                "#app > div.app-wrapper > div.wrap > div.viewContainer.heightAbsolutely > div > div.graph-wrap > div > div > section.title > div.title-fr > div > div"
            );
            if(!read){
                return
            }
            if (typeof read[0] == "undefined") {
                return;
            }
            const readText = read[0].innerHTML
            if(readText){
                if(readText.toString() === '已读'){
                    window.clearInterval(readInterval);
                    window.location.replace(GM_getValue("rowUrl"));
                }
            }
        }, 1000);

        // 为了防止页面假死,定时刷新一下页面
        setTimeout(function () {
            // 如果保存了课程列表路径就回退的课程列表页面
            if(GM_getValue("rowUrl")){
                window.location.replace(GM_getValue("rowUrl"));
            }
            location.reload()
        },reloadTime * 60 * 1000);
    };
})();

代码比较简单,简单的逻辑判断,最难的一个是视频倍速播放,这个 video 倍速播放是锁住的,调整倍速播放的属性是无效的,我就想直接触发它提供的二倍速也行,没想到这个也很困难,视频倍速播放的按钮,首先需要触发鼠标悬浮才有效,这就无法实现自动化的效果了。因为我对 js 的不熟悉,在网上也没有找到解决办法,尝试了各种办法都没有效果,本来一天就能弄完,为了这一个功能,又搭进去好几天\~\~\~,最后是想到去读 greasfork 上其他人实现这种功能的代码,没想到还真让我找到了,就是下面这一段珍贵的代码:

function fun(className, selector)
{
    var mousemove = document.createEvent("MouseEvent");
    mousemove.initMouseEvent("mousemove", true, true, unsafeWindow, 0, 10, 10, 10, 10, 0, 0, 0, 0, 0, null);
    document.getElementsByClassName(className)[0].dispatchEvent(mousemove);
    document.querySelector(selector).click();
}

先触发 dom 元素的 mousemove 事件

之前在网上也有看到过类似的描述,只是我不知道这个 mousemove.initMouseEvent()怎么传参,再加上控制台报错,我就觉得这种方法不行,没想到也是解决办法是远在天边,近在眼前,不过好歹绕回来了。

6、注解介绍

代码的前面被 \==UserScript\== 包裹的注释,包含了一些注解,定义了脚本的一些元信息,接下来介绍几个常用的注解,更多的注解信息可以访问官方文档:https://www.tampermonkey.net/documentation.php

  1. @name 定义脚本的名字
  2. @version 定义脚本的版本,在有代码更新的时候,需要改变版本的值,这样才能将代码推送给用户
  3. @description 脚本的描述
  4. @author 作者名字
  5. @icon 脚本的图标,可以是 url 地址,也可以是图片的 base64 编码
  6. @grant 获取权限,使用它可以获取油猴提供的 API
  7. @match 脚本生效的网址匹配,只有符合匹配规则的网址,才会执行脚本
  8. @run-at 脚本代码执行的时间,有好几个值,这里我用的是 document-end,即当所有的 dom 元素加载完毕之后再执行代码
  9. @license 开源许可证,这里我用的是 MIT,即所有人都可查看和修改代码
  10. @require 引入外部的 js,这里我引入了 jQuery

7、脚本的发布

前往 https://greasyfork.org/zh-CN 网址,登录,点击自己的头像即可发布自己编写的脚本。

填入脚本源码和脚本使用说明即可。

8、脚本使用

8.1、功能实现

目前已实现:

  1. 当进入课程界面,查找未开始和已经有完成度的课程和未读的材料,自动看视频,直到所有视频看完为止
  2. 视频界面,自动播放视频、静音、5秒之后默认开启二倍速

未实现:

  1. 答题,所有习题都会跳过,需要手动答题

只是做了些自动化的处理,理论上是不会有风险的(当然只是理论上,自行考虑 \^_\^)

8.2、使用

本脚本只适用于华南师范大学长江雨课堂

8.3、安装油猴

以edge浏览器为例,在扩展商店搜索tampermonkey,安装油猴插件

image-20221003232246888.png

8.4、安装脚本

在 greasyfork(https://greasyfork.org/zh-CN) 网站,搜索scnu华南师范大学网课脚本关键字,安装脚本

image-20221003234101801.png

image-20221003234133621.png

8.5、脚本运行

前往 scnu 华南师范大学 长江雨课堂(https://scnuyjs.yuketang.cn/pro/portal/home/),登录,进入课程界面,即可开始刷课
注意:edge 浏览器需要允许网站弹窗(注意地址栏的提示)脚本才能执行跳转,这个很关键,不然脚本无法正常执行。

image-20221003232615741.png

8.6、参数调整

总共有两个参数可以调整

  • 一个是页面刷新的时间,防止网络不好或者其它原因造成页面假死,默认为10分钟。
  • 一个是视频倍速的速率,有四个值,1、1.25、1.5、2;默认为 2

可自己在代码编辑进行修改

image-20221008084124867

免费评分

参与人数 5吾爱币 +4 热心值 +4 收起 理由
karmaL + 1 热心回复!
IT12345 + 1 + 1 谢谢@Thanks!
IDEA-568 + 1 我很赞同!
love008 + 1 + 1 以后多写猿猴原创文章
yjn866y + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

雾都孤尔 发表于 2022-11-5 23:44
上网课还是能节省出时间的
dft2010 发表于 2022-11-5 23:44
千城忆梦 发表于 2022-11-6 00:17
遇到校友了,前两年我毕业时还没这玩意……
wojiaolijiaxing 发表于 2022-11-6 07:03
节省了时间去干别的了
nh5089 发表于 2022-11-6 07:54
感谢楼主分享,虽然看不懂,但是觉得都是人才,

这个论坛真是一个好论坛,自动我加入进来,感觉全是人才。
feiyu361 发表于 2022-11-6 09:13
这个好啊,可以无线的刷课了
dqb 发表于 2022-11-6 10:02
太厉害了。
zohoChou 发表于 2022-11-6 10:45
好!!!
yyqx 发表于 2022-11-6 12:31
有用了谢谢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-6-1 10:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表