leqaq 发表于 2022-11-5 23:35

SCNU长江雨课堂网课脚本

本帖最后由 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、脚本代码

```js
// ==UserScript==
// @name         scnu华南师范大学网课脚本
// @namespace    http://tampermonkey.net/
// @version      1.2
// @descriptionscnu 华南师范大学 长江雨课堂 网课自动化脚本
// @AuThor       hqzqaq
// @Icon         https://statics.scnu.edu.cn/statics/images/favicon.ico
// @grant      GM_getValue
// @grant      GM_setValue
// @grant      unsafeWindow
// @match      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;
    // 视频播放速率,可选值 ,默认为二倍速
    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).dispatchEvent(mousemove);
            document.querySelector(selector).click();
      }

      // 加速
      function speed() {
            let keyt = '';
            if(rate === 2 || rate === 1){
                keyt = ""
            }else{
                keyt = ""
            }
            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;
                  const textContent = element.textContent;
                  //const textContent = ''
                  if (textContent === "未开始" || textContent === "未读") {
                        // 判断是否是习题
                        if(elTooltipItemList.innerText.indexOf('习题')!= -1){
                            continue;
                        }
                        // 判断是否已过学习时间
                        if (elTooltipItemList.children.children.innerText.indexOf("已过") != -1) {
                            continue;
                        }
                        window.clearInterval(getElementInterval);
                        GM_setValue("rowUrl", window.location.href.toString());
                        // 网课页面跳转
                        elTooltipItemList.click();
                        window.close();
                        break;
                  }
                }
            }
      }, 1000);

      let video;
      const videoPlay = setInterval(function () {
            // 获取播放器
            video = document.getElementsByClassName("xt_video_player");
            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 == "undefined") {
                return;
            }
            const videoText = completeness.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 == "undefined") {
                return;
            }
            const readText = read.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 上其他人实现这种功能的代码,没想到还真让我找到了,就是下面这一段珍贵的代码:

```js
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).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`,安装油猴插件



### 8.4、安装脚本

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





### 8.5、脚本运行

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



### 8.6、参数调整

总共有两个参数可以调整

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

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

!(https://gis-visualization.oss-cn-beijing.aliyuncs.com/typoraImg/image-20221008084124867.png)

雾都孤尔 发表于 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

有用了谢谢
页: [1]
查看完整版本: SCNU长江雨课堂网课脚本