FuckUlearning

优学院视频刷课脚本

// ==UserScript==
// @name         FuckUlearning
// @description  优学院视频刷课脚本
// @version      1.0.1
// @author       Nanashi
// @match        *://ua.ulearning.cn/*
// @icon         https://raw.githubusercontent.com/Nanashi-Development/FuckULearning/main/logo.jpg
// @supportURL   https://github.com/Nanashi-Development/FuckULearning/issues
// @homepageURL  https://github.com/Nanashi-Development/FuckULearning
// @grant        none
// @namespace https://gf.qytechs.cn/users/1518317
// ==/UserScript==


// 等待函数
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
// 等待指定元素加载完成
async function waitForElement(selector, timeout = 10000) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeout) {
        const element = document.querySelector(selector);
        if (element) {
            return element; // 元素已加载,返回该元素
        }
        await sleep(500); // 每隔 500 毫秒检查一次
    }
    console.log(`等待元素超时:${selector}`);
    return null; // 超时未找到元素
}



// 判断是否是视频课程
function is_video_course() {
    const playButton = document.querySelector('div.mejs__overlay-button[role="button"][aria-label="播放"]');
    const isVideo = !!playButton;
    console.log(isVideo ? '检测到视频课程' : '未检测到视频课程');
    return isVideo;
}
// 自动检测页面中的视频播放器并逐个播放
async function playAllVideos() {
    // 获取所有 class="video-element" 的元素
    const videoElements = document.querySelectorAll('.video-element');

    // 遍历每个 video-element
    for (let i = 0; i < videoElements.length; i++) {
        const videoElement = videoElements[i];

        // 定位 <div class="mejs__container"> 并获取其 id
        const container = videoElement.querySelector('.mejs__container');
        const videoId = container ? container.id : null; // 如果找到容器,则获取其 id,否则为 null

        if (!videoId) {
            console.log('未找到视频容器的 id,跳过当前视频');
            continue;
        }

        console.log(`当前视频的 videoId:${videoId}`);

        // 判断视频是否已经看过了
        const videoInfo = videoElement.querySelector('.video-info');
        if (videoInfo) {
            const finishedSpan = videoInfo.querySelector('span[data-bind="text: $root.i18nMessageText().finished"]');
            if (finishedSpan) {
                console.log(`视频已看完,视频ID:${videoId}`);
                continue;
            } else {
                console.log(`视频未看完,视频ID:${videoId}`);
            }
        } else {
            console.log(`未找到 video-info 元素,以未看完模式继续,视频ID:${videoId}`);
        }

        // 未看过的视频,进行观看
        const playButtonSelector = `#${videoId} .mejs__overlay-button[aria-label="播放"]`;
        const playButton = await waitForElement(playButtonSelector, 5000);
        const realVideoElement = document.querySelector(`#${videoId} video`);

        if (playButton) {
            const isVisible = window.getComputedStyle(playButton).display !== 'none';
            if (isVisible) {
                playButton.click();
                console.log(`已点击播放按钮,开始播放视频:${videoId}`);

                // 点击二倍速播放按钮
                try {
                    await sleep(1000); // 等待播放器加载
                    const speedButton = document.querySelector(`#${videoId} label.mejs__speed-selector-label.mejs__speed-selected`);
                    if (speedButton && speedButton.textContent === '2.00x') {
                        console.log(`当前已是二倍速播放:${videoId}`);
                    } else {
                        // 如果当前不是2倍速,尝试寻找并点击2倍速选项
                        const speedSelector = document.querySelector(`#${videoId} .mejs__speed-selector`);
                        if (speedSelector) {
                            const allSpeedOptions = speedSelector.querySelectorAll('label.mejs__speed-selector-label');
                            for (const option of allSpeedOptions) {
                                if (option.textContent.trim() === '2.00x') {
                                    option.click();
                                    console.log(`已切换到二倍速播放:${videoId}`);
                                    break;
                                }
                            }
                        } else {
                            console.log(`未找到速度选择器,使用默认播放速度:${videoId}`);
                        }
                    }
                } catch (error) {
                    console.log(`设置播放速度时出错:${videoId},错误:${error.message}`);
                }

                // 等待视频播放完成
                let isFinished = false;
                while (!isFinished) {
                    // 是否出现暂停弹窗
                    await sleep(1000);
                    needRepaly = handle_modal(
                        'span[data-bind="text: $root.i18nMsgText().walkingTooLong"]',
                        () => document.querySelector('button.btn-submit[data-bind="text: $root.i18nMsgText().continueStudy"]')?.click(),
                        '暂停弹窗已存在,点击继续学习按钮'
                    );

                    await sleep(1000);
                    if (needRepaly) {
                        playButton.click();
                        console.log(`已重新开始播放视频:${videoId}`);
                    }

                    // 检查视频是否播放完成
                    await sleep(1000);
                    console.log(`视频状态: currentTime=${realVideoElement.currentTime}, duration=${realVideoElement.duration}, ended=${realVideoElement.ended}`);
                    if (realVideoElement.ended || realVideoElement.currentTime >= realVideoElement.duration - 1 || realVideoElement.currentTime === 0) {
                        console.log(`视频 ${videoId} 播放完成`);
                        isFinished = true;
                        break;
                    }
                    console.log(`视频 ${videoId} 未播放完成,继续等待...`);
                    
                }
            } else {
                console.log(`播放按钮不可见,无法播放视频:${videoId}`);
            }
        } else {
            console.log(`未找到播放按钮,跳过视频:${videoId}`);
        }
    }

    console.log('本页面所有视频播放完成');
}
// 弹窗处理函数
function handle_modal(selector, action, logMessage) {
    const modalElement = document.querySelector(selector);
    if (modalElement && modalElement.offsetParent !== null) {
        console.log(logMessage);
        action();
        return true;
    }
    return false;
}
// 点击下一页按钮&检查是否完成
async function goto_next_page_and_check_finish() {
    const nextPageButton = document.querySelector('.btn-tip span[data-bind="text: i18nMessageText().nextPage"]');
    if (nextPageButton) {
        nextPageButton.click();
        console.log('已点击下一页按钮');
    } else {
        console.log('未找到下一页按钮');
    }

    await sleep(1000);

    // 处理未完成测试题目弹窗
    handle_modal(
        'span[data-bind="text: $root.i18nMsgText().questionsNotCompleted"]',
        () => document.querySelector('button.btn-hollow[data-bind="text: $root.i18nMsgText().confirmLeave"]')?.click(),
        '未完成题目弹窗已存在,点击确定离开按钮'
    );

    await sleep(1000);

    // 处理未完成章节内容弹窗
    handle_modal(
        'span[data-bind="text: i18nMessageText().incompleteChapter"]',
        () => document.querySelector('button.btn-hollow[data-bind="click: goNextPage"]')?.click(),
        '未完成章节内容弹窗已存在,点击继续下一章按钮'
    );

    await sleep(1000);

    // 处理未完成章节内容2弹窗
    const isFinished = handle_modal(
        'span[data-bind="text: i18nMessageText().incompleteChapter2"]',
        () => document.querySelector('button.btn-hollow[data-bind="click: closeStatPage"]')?.click(),
        '未完成章节内容2弹窗已存在,点击关闭按钮'
    );

    return isFinished; // 返回是否完成
}



// 创建可视化弹窗
function createControlPanel() {
    const panel = document.createElement('div');
    panel.id = 'control-panel';
    panel.style.position = 'fixed';
    panel.style.top = '50px';
    panel.style.left = '50px';
    panel.style.width = '300px';
    panel.style.height = '300px';
    panel.style.backgroundColor = '#f9f9f9';
    panel.style.border = '1px solid #ccc';
    panel.style.borderRadius = '8px';
    panel.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
    panel.style.zIndex = '9999';
    panel.style.resize = 'both';
    panel.style.overflow = 'hidden'; // 隐藏溢出内容
    panel.style.padding = '10px';

    // 添加标题
    const title = document.createElement('div');
    title.innerText = '刷课控制面板';
    title.style.fontSize = '16px';
    title.style.fontWeight = 'bold';
    title.style.marginBottom = '10px';
    title.style.cursor = 'move';
    panel.appendChild(title);

    // 添加启动按钮
    const startButton = document.createElement('button');
    startButton.innerText = '启动';
    startButton.style.marginRight = '10px';
    startButton.onclick = async () => {
        logMessage('启动刷课程序...');
        await main(); // 调用主程序
    };
    panel.appendChild(startButton);

    // 添加停止按钮
    const stopButton = document.createElement('button');
    stopButton.innerText = '停止';
    stopButton.onclick = () => {
        logMessage('停止刷课程序...');
        location.reload(); // 刷新页面以停止程序
    };
    panel.appendChild(stopButton);

    // 添加日志显示区域
    const logContainer = document.createElement('div');
    logContainer.id = 'log-container';
    logContainer.style.marginTop = '10px';
    logContainer.style.height = 'calc(100% - 80px)'; // 自适应高度,减去标题和按钮的高度
    logContainer.style.overflowY = 'auto';
    logContainer.style.backgroundColor = '#fff';
    logContainer.style.border = '1px solid #ddd';
    logContainer.style.padding = '5px';
    logContainer.style.fontSize = '12px';
    logContainer.style.color = '#333';
    panel.appendChild(logContainer);

    // 添加拖动功能
    let isDragging = false;
    let offsetX, offsetY;

    title.onmousedown = (e) => {
        isDragging = true;
        offsetX = e.clientX - panel.offsetLeft;
        offsetY = e.clientY - panel.offsetTop;
        document.onmousemove = (e) => {
            if (isDragging) {
                panel.style.left = `${e.clientX - offsetX}px`;
                panel.style.top = `${e.clientY - offsetY}px`;
            }
        };
        document.onmouseup = () => {
            isDragging = false;
            document.onmousemove = null;
            document.onmouseup = null;
        };
    };

    // 将弹窗添加到页面
    document.body.appendChild(panel);
}
// 日志记录函数
function logMessage(message) {
    const logContainer = document.getElementById('log-container');
    if (logContainer) {
        const logEntry = document.createElement('div');
        logEntry.innerText = `[${new Date().toLocaleTimeString()}] ${message}`;
        logContainer.appendChild(logEntry);
        logContainer.scrollTop = logContainer.scrollHeight; // 自动滚动到底部
    }
}



// 主程序
async function main() {
    logMessage('刷课程序启动...');

    let allFinished = false;
    while (!allFinished) {
        if (is_video_course()) {
            logMessage('检测到视频课程,准备播放所有视频...');
            await playAllVideos(); // 播放所有视频
            logMessage('本页面所有视频播放完成,准备进入下一页...');
            allFinished = await goto_next_page_and_check_finish();
        } else {
            logMessage('检测到非视频课程,准备进入下一页...');
            await sleep(1000);
            allFinished = await goto_next_page_and_check_finish();
        }
    }

    logMessage('刷课程序执行完毕');
}
// 调用函数创建控制面板
createControlPanel();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址