Greasy Fork

Greasy Fork is available in English.

[25-05-21] 青书学堂自动看课

自动看视频+同时获取网络资料学习积分

当前为 2025-05-21 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         [25-05-21] 青书学堂自动看课
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  自动看视频+同时获取网络资料学习积分
// @author       Hu5
// @run-at       document-start
// @match        https://degree.qingshuxuetang.com/*/Student/Course/CourseShow*
// ==/UserScript==

(function() {
    'use strict';
    // 跳过iFrame
    if (window.top !== window.self) return
    function getAllNodeId() {
        // 获取所有的课程ID
        const allElements = document.querySelectorAll('*');
        const matchingIds = [];
        for (let i = 0; i < allElements.length; i++) {
            const element = allElements[i];
            if (element.id && element.id.startsWith("courseware-kcjs_")) {
                matchingIds.push(element.id.replace("courseware-", ""));
            }
        }
        return matchingIds;
    }

    async function getEbookId() {
        // 用于获取需要的文档ID
        const courseId = (new URL(document.URL)).searchParams.get("courseId")
        const res = await fetch(`/hkd/Student/Course/CourseEbooks?order=asc&offset=0&limit=10&courseId=${courseId}`)
        const resJson = await res.json()
        console.log(`[Ebook] 成功获取网络资料`,resJson.data.data.rows)
        return resJson.data.data.rows[0].materialId
    }

    async function startEbookStudy(classId, courseId, ebookId, periodId) {
        // 开始学习 并记录 获取接口返回的记录ID
        console.log(`[Ebook] 开始学习`)
        const res = await fetch(`/hkd/Student/Course/UploadStudyRecordBegin`, {
            method: "POST",
            headers: {
                "Content-Type": 'application/json',
            },
            body: JSON.stringify({
                "classId": classId,
                "courseId": courseId,
                "contentId": ebookId,
                "contentType": 12,
                "periodId": periodId,
                "position": 0,
                "detectId": null
            })
        })
        const resJson = await res.json()
        const recordId = resJson.data
        console.log(`[Ebook] ${recordId}`)
        // 配置一个定时器 每两分钟上传学习记录
        console.log(`[Ebook] 定时上传学习记录`)
        setInterval(function() {
            fetch(`hkd/Student/Course/UploadStudyRecordContinue`, {
                method: "POST",
                headers: {
                    "Content-Type": 'application/x-www-form-urlencoded',
                },
                body: `recordId=${recordId}&end=false&position=0&timeOutConfirm=false`
            })
        }, 12e4)
        // 增加一个退出回调,页面退出就结束本次学习
        window.addEventListener("beforeunload", function() {
            const fd = new FormData();
            fd.append('recordId', recordId);
            fd.append('end', true);
            fd.append('position', 0);
            fd.append('timeOutConfirm', false);
            navigator.sendBeacon(`/hkd/Student/Course/UploadStudyRecordContinue`, fd)
        })

        return recordId
    }

    async function ebook() {
        // 获取学习积分
        const urlParams = (new URL(document.URL)).searchParams
        const classId = urlParams.get("teachPlanId")
        const courseId = urlParams.get("courseId")
        const periodId = urlParams.get("periodId")
        const ebookId = await getEbookId()
        startEbookStudy(classId, courseId, ebookId, periodId)

    }

    function playVideo(videoElm) {
        // 当前的视频ID
        const nodeId = (new URL(document.URL)).searchParams.get("nodeId")
        const kcjsIds = getAllNodeId()
        // 下一节课的ID
        const nextNodeId = kcjsIds[kcjsIds.indexOf(nodeId) + 1]

        // 自动播放
        videoElm.autoplay = true
        // 慢倍速播放凑时长
        videoElm.playbackRate = 1;
        // 开始播放
        const playHandler = setInterval(()=>{
            // 静音
            videoElm.muted = true;
            videoElm.play()
        }, 100)
        videoElm.onplaying = () => {
            clearInterval(playHandler)
        }
        // 顺便获取网络学习积分
        ebook()
        // 看完自动下一课
        videoElm.onended = () => {
            // 如果还有下一节课,继续看
            if (nextNodeId) {
                // 调用页面方法点击下一课
                window.CoursewareNodesManager.onMenuClick(nextNodeId)
            } else {
                // 没有就是看完了,不再继续
                console.log(`[结束] 看完了,不再继续`);
            }
        }
    }

    // 其实你播不播放视频都行
    // 只要调用这里的JS,例如 uploadStudyRecordBegin 进行学习记录的上传就行
    // https://degree.qingshuxuetang.com/resources/default/ui/lib/onlinecourse/studyRecordService.js?v=23.04.0
    async function main() {
        // 获取所有课程ID
        const videoElem = document.querySelector("video.vjs-tech")
        // 两种情况 1.页面已加载video(调试时场景) 2.尚未动态加载 均直接播放
        if (videoElem) {
            console.log(`[已有Video] 播放视频`)
            playVideo(videoElem)
        } else {
            // 播放容器
            let videoPlayer = document.querySelector("#playerContainer")

            // 监视HTML变化 以找出动态加载的video标签
            const observer = new MutationObserver(async (mutationsList, observer) => {
                for (const mutation of mutationsList) {
                    if (mutation.type === 'childList') {
                        // 查找视频元素
                        const videoElm = mutation.target.querySelector('video')
                        if (videoElm) {
                            // 播放视频
                            playVideo(videoElm)
                            // 找到关闭监视
                            return observer.disconnect()
                        }
                    }
                }
            }
            )
            // 存在播放容器开始监视
            if (videoPlayer) {
                observer.observe(videoPlayer, {
                    childList: true,
                    subtree: true
                });
            }
        }
        // 检测概述并跳过
        const nodeId = (new URL(document.URL)).searchParams.get("nodeId")
        if (!nodeId.indexOf(`jbxx`)) {
            console.log(`[Main] 跳过概述,直达视频`)
            const kcjsIds = getAllNodeId()
            return window.CoursewareNodesManager.onMenuClick(kcjsIds[0])

        }
        console.log(`[Main] 开始看课`)

    }

    window.addEventListener("load",main)
}
)();