Greasy Fork

Greasy Fork is available in English.

学习通刷课助手-自动静音,防止鼠标移出暂停,章节结束自动跳转下一节

学习通课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停。

当前为 2024-11-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         学习通刷课助手-自动静音,防止鼠标移出暂停,章节结束自动跳转下一节
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  学习通课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停。
// @author       Sweek
// @match        *://*.chaoxing.com/*
// @license      GPLv3
// @icon         https://www.google.com/s2/favicons?sz=64&domain=csdn.net
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// ==/UserScript==


// 定义全局变量
let currentTime = null // 当前视频当前播放节点
let duration = null // 当前视频总长度
let progress = null // 当前视频播放进度
let playbackRate = 1 // 当前视频播放倍速

// 课程章节相关数据
let courseName = null // 当前课程名称
let courseId = null // 当前课程id
let chapterInfo = [] // 当前课程所有章节数据
let currentChapterId = null // 当前所在章节id
let currentChapterName = null // 当前所在章节名称
let allChapterName = []  // 所有章节名称
let currentChapterTabArr = [] // 当前页面所有的分栏
let currentChapterTabName = '' // 当前页面激活的分栏名称
let videoProgressId = '' // 定时监听页面内容监听事件

// 获取当前页面的 URL
url = ''
chapterId = ''


let arrayEchelon = []; // 顺序执行梯队,初始化为空数组
const dealEvent = new Event("redeal", { bubbles: false, cancelable: false });


// 页面模板部分
// 页面模板部分
// 页面模板部分

// 页面样式
var popCSs = `
#my-window {
    position: fixed;
    top: 5px;
    left: 20px;
    width: 300px;
    height: auto;
    background-color: rgba(247, 247, 247, 1);
    border: 1px solid #fff;
    border-radius: 5px;
    z-index: 9999;
    overflow: hidden;
  }
  
  #my-window .header {
    background-color: #4497fa;
    color: #fff;
    padding: 5px;
    font-size: 16px;
    font-family: 'fangsong';
    font-weight: bold;
    border-radius: 5px;
    cursor: move;
    height: 25px;
    width: 300px;
  }

  #my-window .content {
    width: 300px;
    height: 570px;
  }

  #my-window .content .content-title {
    height: 22px;
    width: 280px;
    background-color: #dadada;
    line-height: 22px;
    padding-left: 5px;
    font-size: 12px;
    font-family: 'fangsong';
    font-weight: 600;
    boder-radius: 5px;
    border-left: 4px solid #2196f3;
    border-right: 4px solid #dadada;
    margin-top: 5px;
  }

  #my-window .content .content-notice {
    height: 80px;
    width: 280px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 5px;
    padding: 5px;
    margin-top: 5px;
  }

  #my-window .content .content-process {
    height: 60px;
    width: 280px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 5px;
    padding: 5px;
    margin-top: 5px;
  }

  #my-window .content .content-log {
    height: 120px;
    width: 280px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 5px;
    padding: 5px;
    margin-top: 5px;
  }
  
  #my-window .content .content-set {
    height: 130px;
    width: 280px;
    overflow: auto;
    border: 1px solid gray;
    border-radius: 5px;
    padding: 5px;
    margin-top: 5px;
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
  }

  #my-window .content .content-set .content-set-content {
    width: 280px;
    display: flex;
    justify-content: space-between;
  }

  #my-window .content .content-set .content-set-content #email-input {
    width: 200px;
    border-radius: 5px;
    border: 1px solid gray;
  }


  #my-window .content .content-set #save-btn {
    color: gray;
    width: 100%;
    margin-top: 10px;
    border-radius: 5px;
    border: 1px solid gray;
  }

  #save-btn:hover {
    background-color: #e3e3e3!important;
  }

  #my-window .resizer {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 20px;
    height: 20px;
    background-color: #2196f3;
    cursor: se-resize;
    border-radius: 0px;
    z-index: 1;
  }
  
  #hide-btn {
    height: 25px;
    width: auto;
    float: right;
    margin-right: 10px;
    background-color: #fff;
    border: 1px solid gray;
    border-radius: 5px;
    font-size: 12px;
    padding: 0 5px;
    font-family: 'fangsong';
  }

  #hide-btn:active {
    background-color: #4497fa;
  }
`

// 页面Html
var popHtml = `
<div class="header">学习通助手
  <button id="hide-btn">显示/隐藏</button>
</div>
<div class="content" id="my-window-content">
  <div class="resizer" style="display: none;"></div>
  <div class="row" style="border: 1px solid #ccc; padding: 5px;">
    <div class="content-title">公告</div>
    <div class="content-notice" id="content-notice">
    </div>
    <div class="content-title">播放进度</div>
    <div class="content-process" id="content-process"></div>
    <div class="content-title">执行日志</div>
    <div class="content-log" id="content-log"></div>
    <div class="content-title">配置</div>
    <div class="content-set" id="content-set">        
    <div>该邮箱地址用来登录app网站,该邮箱需要和网站注册邮箱保持一致,不可为空,填写完,点击保存,可在网站查看课程进度,二维码:
      <a style="color: dodgerblue;cursor: pointer;" id="Qcode">点击查看,微信扫码</a>
    </div>
      <div class="content-set-content">
        <label for="email-input">邮箱:</label>
        <input type="email" id="email-input" placeholder="请输入邮箱地址">
      </div>
      <button id="save-btn">保存</button>
    </div>
  </div>
</div>
`
// 初始化添加页面弹窗以及悬浮球
function initPopup() {
  // 添加CSS样式
  GM_addStyle(popCSs);
  // 创建窗口元素
  const myWindow = document.createElement("div");
  myWindow.id = "my-window";
  myWindow.innerHTML = popHtml;
  // 获取页面body元素
  const body = document.getElementsByTagName("body")[0];
  // 添加窗口和悬浮球到页面
  body.appendChild(myWindow);
 // 绑定隐藏窗口按钮的click事件
 const hideBtn = document.querySelector("#hide-btn");
 hideBtn.addEventListener("click", hideWindow);
// 获取弹窗内容
var htmlContent = '<div style="width: 800px; height: 400px;display: flex;justify-content: center;align-items: center;"><img style="width: 400px; height: 400px;" src="https://www.sweek.top/api/preview/Qcode.png" alt="微信扫码"><img style="height: 400px;" src="https://www.sweek.top/api/preview/Qrcode.jpg" alt="微信扫码"></div>';

// 点击链接显示弹窗
document.getElementById('Qcode').addEventListener('click', function() {
  showCustomPopup(htmlContent, 800, 400);
});
 

// 获取头部元素
const header = myWindow.querySelector('.header');

// 处理移动事件
let isDragging = false;
let mouseX = 0;
let mouseY = 0;

header.addEventListener('mousedown', function (e) {
  e.preventDefault();
  isDragging = true;
  mouseX = e.clientX;
  mouseY = e.clientY;
});

document.addEventListener('mousemove', function (e) {
  if (isDragging) {
    const deltaX = e.clientX - mouseX;
    const deltaY = e.clientY - mouseY;
    const newLeft = Math.min(
      Math.max(0, myWindow.offsetLeft + deltaX),
      window.innerWidth - myWindow.offsetWidth
    );
    const newTop = Math.min(
      Math.max(0, myWindow.offsetTop + deltaY),
      window.innerHeight - myWindow.offsetHeight
    );
    myWindow.style.left = newLeft + 'px';
    myWindow.style.top = newTop + 'px';
    mouseX = e.clientX;
    mouseY = e.clientY;
  }
});

document.addEventListener('mouseup', function () {
  isDragging = false;
});

}

// 隐藏窗口函数
function hideWindow() {
  var myWindowContent = document.getElementById("my-window-content");
  var showPop = myWindowContent.style.display
  if (showPop == '' || showPop == 'block') {
    myWindowContent.style.display = "none";
  } else {
    myWindowContent.style.display = "block";
  }
}

function takeEmail() {
  // 获取邮箱输入框和保存按钮
  const emailInput = document.getElementById("email-input");
  const saveBtn = document.getElementById("save-btn");

  // 当保存按钮被点击时
  saveBtn.addEventListener("click", function() {
    // 获取输入的邮箱地址
    const email = emailInput.value.trim();
    // 检查邮箱地址是否有效
    if (!isValidEmail(email)) {
      notify('请输入有效的邮箱地址!', 2500)
      return;
    }
    
    const url = 'https://www.sweek.top/api/checkEmailExists';
    const data = { 
      email
    };
    if(email) {
      fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
        },
        body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
      }).then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json(); // 解析 JSON 响应数据
      }).then(data => {
        if(data.exists){
          // 将邮箱地址存储到本地存储中
          GM_setValue("savedEmail", email);
          // 提示保存成功
          notify('保存成功!课程信息将同步到该邮箱账号', 2500)
        } else {
          notify('邮箱账号尚未注册,请扫码前往留言信箱网站注册', 2500)
        }
      }).catch(error => {
        console.error('Error:', error);
      });
    }

  });

  // 页面加载时,尝试从本地存储中获取已保存的邮箱地址并反显到输入框中
  const savedEmail = GM_getValue("savedEmail");
  if (savedEmail) {
    emailInput.value = savedEmail;
  }
}

// 邮箱地址验证函数
function isValidEmail(email) {
  // 此处可以使用正则表达式等方式进行邮箱地址的验证
  // 这里简单地判断邮箱是否包含 '@' 符号
  return email.includes("@");
}


// 封装页面组件方法
// 封装页面组件方法
// 封装页面组件方法

// 页面弹窗显示
function showCustomPopup(htmlContent, width, height) {
    // 创建弹窗容器
    var popupContainer = document.createElement('div');
    popupContainer.className = 'custom-popup-container';
    // 设置弹窗容器样式
    popupContainer.style.position = 'fixed';
    popupContainer.style.top = '50px';
    popupContainer.style.left = '0';
    popupContainer.style.width = '100%';
    popupContainer.style.height = 'calc(100% - 50px)';
    popupContainer.style.display = 'flex';
    popupContainer.style.justifyContent = 'center';
    popupContainer.style.alignItems = 'center';
    popupContainer.style.zIndex = '999999';

    // 创建弹窗内容容器
    var popupContent = document.createElement('div');
    popupContent.className = 'custom-popup-content';
    // 设置弹窗内容样式
    popupContent.style.width = width + 'px';
    popupContent.style.height = height + 'px';
    popupContent.style.backgroundColor = '#fff';
    popupContent.style.border = '1px solid #ccc';
    popupContent.style.borderRadius = '5px';
    popupContent.style.position = 'relative';

    // 创建关闭按钮
    var closeButton = document.createElement('button');
    closeButton.innerHTML = '关闭';
    closeButton.style.position = 'absolute';
    closeButton.style.top = '5px';
    closeButton.style.right = '5px';
    closeButton.style.padding = '5px 10px';
    closeButton.style.backgroundColor = '#ccc';
    closeButton.style.border = 'none';
    closeButton.style.borderRadius = '3px';
    closeButton.style.cursor = 'pointer';
    closeButton.style.fontFamily = 'Arial, sans-serif';

    // 关闭按钮点击事件处理
    closeButton.addEventListener('click', function() {
        popupContainer.remove();
    });

    // 设置弹窗内容
    popupContent.innerHTML = htmlContent;

    // 将关闭按钮和弹窗内容添加到弹窗容器中
    popupContent.appendChild(closeButton);
    popupContainer.appendChild(popupContent);

    // 将弹窗容器添加到页面中
    document.body.appendChild(popupContainer);
}

// 页面通知提示
function notify(text, time) {
// 创建通知元素
var notification = document.createElement('div');
notification.className = 'notification';
// 设置通知内容
notification.innerHTML = '<div class="notification-content"><h2 style="font-size: 16px;font-weight: bold;color: #307dff;font-family: fangsong;">' + '学习通助手提示' + '</h2><p style="font-family: fangsong;font-size: 13px;font-weight: bold;">' + text + '</p></div>';
// 将通知添加到页面中
document.body.appendChild(notification);
// 设置通知样式
notification.style.position = 'fixed';
notification.style.top = '50px';
notification.style.left = '-400px'; // 从左边弹出
notification.style.transform = 'translateY(-50%)'; // 垂直居中
notification.style.transition = 'left 0.5s ease-in-out'; // 添加过渡效果
notification.style.zIndex = '999999';
notification.style.backgroundColor = '#fff';
notification.style.border = '1px solid #ccc';
notification.style.padding = '10px';
notification.style.borderRadius = '5px';
notification.style.lineHeight = '25px';

// 等待一小段时间后,移动通知到左边
setTimeout(function() {
    notification.style.left = '20px'; // 移动到左边
}, 100);

// 设置定时器,在指定时间后移除通知
setTimeout(function() {
    // 移动通知到左边以外
    notification.style.left = '-400px';
    // 等待过渡效果完成后,移除通知元素
    setTimeout(function() {
    notification.remove();
    }, 500);
}, time);
}

// 添加执行日志
function addlog(str, color) {
var _time = new Date().toLocaleTimeString()
var contentLog = window.top.document.querySelector('.content-log');
var newContent = '<p style="color: ' + color + ';">[' + _time + ']' + str + '</p><hr>';
contentLog.innerHTML += newContent;
// 将滚动条滚动到底部
contentLog.scrollTop = contentLog.scrollHeight;
}


/** 监听事件 */
document.addEventListener("redeal", () => {
    dealAnsEchelon(arrayEchelon);
});

/** 初始化 */
function initAll() {
	  addlog('正在获取当前页面的任务...');

    const arrayAns = document.querySelectorAll(".ans-attach-ct");
    const taskCount = arrayAns.length;
		// addlog(`找到的任务数量: ${taskCount}`);
    if (taskCount > 0) {
        try {
            const arrayType = getIframesType(arrayAns);
            // console.log('任务类型数组:', arrayType);

            const arrayDocument = getAllIframesDocument(arrayAns);
            // console.log('任务文档数组:', arrayDocument);

            arrayEchelon = distributeAns(arrayType, arrayDocument);
						addlog(`当前页面任务数量为: ${arrayEchelon.length}`);

            // 触发处理任务的事件
            document.dispatchEvent(dealEvent);
        } catch (error) {
            console.error("初始化过程中发生错误:", error);
        }
    } else {
			  addlog("当前页面没有可处理的任务,直接跳过章节");
        // 如果没有任务,
        // skipChapter();
    }
}

/** 工具函数 */
/** 工具函数 */
/** 工具函数 */
// 获取当前时间,年月日时分秒
function getCurrentDateTime() {
	var now = new Date();
	var year = now.getFullYear();
	var month = (now.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始,需要加1,并确保两位数格式
	var day = now.getDate().toString().padStart(2, '0'); // 确保两位数格式
	var hours = now.getHours().toString().padStart(2, '0'); // 确保两位数格式
	var minutes = now.getMinutes().toString().padStart(2, '0'); // 确保两位数格式
	var seconds = now.getSeconds().toString().padStart(2, '0'); // 确保两位数格式

	return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

// 设置播放进度
function setVideoProcess(val1, val2, val3) {
  var _process = window.top.document.querySelector('#content-process');
  var _time = getCurrentDateTime()
  var newContent = '<p style="color: #000;">[' + _time + ']' + '</p><hr><p>' + '播放进度:' + val1 + '</p><hr><p>' + '视频长度:' + val2 + 's' + '/' +val3 + 's' + '</p>';
  _process.innerHTML = newContent;
}

// 获取视频播放进度-定时监听页面内容进行下一步处理
function getVideoProgress() {
  // 同步课程进度
  if(location.pathname == '/mycourse/studentstudy') {
    const emailInput = document.getElementById("email-input");
    const email = emailInput.value.trim();
    const url = 'https://www.sweek.top/api/checkEmailExists';
    const data = { 
      email
    };
    if(email) {
      fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
        },
        body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
      }).then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json(); // 解析 JSON 响应数据
      }).then(data => {
        if(data.exists){  
          syncCourseData()
        } else {
          addlog('同步课程信息失败,该邮箱尚未注册')
        }
      }).catch(error => {
        console.error('Error:', error);
      });
    }

    // 脚本运行过程中如果弹出弹窗,1s后关闭
		const jobFinishTip = document.querySelector(".jobFinishTip");
    if (jobFinishTip) {
      const computedStyle = getComputedStyle(jobFinishTip);
      if (computedStyle.display !== 'none') {
        jobFinishTip.style.display = 'none';
      }
    }
  }
}

// 获取页面url
function getURLInfo() {
  if(location.pathname == '/mycourse/studentstudy') {
    url = location.href
    // 获取问号后面的部分
    var queryString = url.split('?')[1];
    // 将查询字符串拆分为参数对
    var queryParams = queryString.split('&');
    // 创建一个对象来存储参数
    var params = {};
    // 遍历参数对,将它们存储在对象中
    queryParams.forEach(function(queryParam) {
        var parts = queryParam.split('=');
        var key = decodeURIComponent(parts[0]);
        var value = decodeURIComponent(parts[1]);
        params[key] = value;
    });
    chapterId = params['chapterId']
    courseId = params['courseId']
    GM_setValue("courseId", courseId);
  }
}

// 获取当前页面章节小节的所有分栏
function getSubChapter() {
  try {
    if(location.pathname == '/mycourse/studentstudy') {
      var subChapter = window.top.document.querySelector('#prev_tab');
      if(subChapter) {
        currentChapterTabArr = subChapter.querySelectorAll('li');
        currentChapterTabArr.forEach(function(item) {
          if(item.className === 'active') {
            currentChapterTabName = item.innerText
          }
        })
      }
      
    }
  } catch (error) {
    console.error('An error occurred:', error);
    // location.reload(); // 刷新页面
  }
}

// 获取课程所有章节节点数据
function getChapterCodeInfo() {
  if(location.pathname === '/mooc-ans/knowledge/cards') {
    var chapter = window.top.document.querySelectorAll('.posCatalog_select')
    chapterInfo = chapter
    allChapterName=[]

    chapterInfo.forEach(function(item) {
      allChapterName.push({
        id: item.id,
        title: item.innerText,
        active: item.classList.contains('posCatalog_active') ? 1 : 0,
        ifTitle: item.classList.contains('firstLayer') ? 1 : 0,
        status: item.childNodes[3]?.className == 'icon_Completed prevTips' ? 1 : 0
      })
      if (item.classList.contains('posCatalog_active')) {
        currentChapterId = item.id
        GM_setValue("currentChapterId", currentChapterId);
        currentChapterName = item.innerText
        GM_setValue("currentChapterName", currentChapterName);
      }
    });
    GM_setValue("chapterInfo", JSON.parse(JSON.stringify(allChapterName)));
    addlog('当前章节为' + currentChapterName, 'green')
  }
}

// 获取公告数据
function getBoard() {
  fetch('https://www.sweek.top/api/board')
  .then(response => response.json())
  .then(data => {
    // 在这里处理接收到的数据
    var notice = document.querySelector('#content-notice');
    notice.innerHTML = data.text
  })
  .catch(error => {
    // 在这里处理错误
    console.error('Error:', error);
  });
}

// 同步课程数据至数据库
function syncCourseData() {
  const url = 'https://www.sweek.top/api/insertOrUpdateCourse';
  const email = GM_getValue("savedEmail")
  const course_name = GM_getValue("courseName")
  const current_chapter_id = GM_getValue("currentChapterId")
  const current_chapter_name = GM_getValue("currentChapterName")
  const course_id = GM_getValue("courseId")
  const process = document.querySelector('#content-process').innerHTML  
  const chapter_info = GM_getValue("chapterInfo")
  var chapter = window.top.document.querySelectorAll('.posCatalog_select')
  allChapterName = []
  chapter.forEach(function(item) {
    allChapterName.push({
      id: item.id,
      title: item.innerText,
      active: item.classList.contains('posCatalog_active') ? 1 : 0,
      ifTitle: item.classList.contains('firstLayer') ? 1 : 0,
      status: item.childNodes[3]?.className == 'icon_Completed prevTips' ? 1 : 0
    })
    if (item.classList.contains('posCatalog_active')) {
      currentChapterId = item.id
      GM_setValue("currentChapterId", currentChapterId);
      currentChapterName = item.innerText
      GM_setValue("currentChapterName", currentChapterName);
    }
  });
  const data = { 
    email,
    course_id: course_id || '',
    course_name: course_name || '测试',
    chapter: JSON.stringify(allChapterName),
    current_chapter: current_chapter_id + ',' + current_chapter_name,
    process
  };
	// 播放异常刷新页面
  // if(process.includes("NaN")) {
  //   location.reload(); // 刷新页面
  //   return
  // }
  if(email && process) {
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
      },
      body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
    }).then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json(); // 解析 JSON 响应数据
    }).then(data => {
      addlog('同步课程信息成功')
    }).catch(error => {
      console.error('Error:', error);
    });
  }
}

// 获取任务属性
function getIframesType(arrayAns) {
    return Array.from(arrayAns)
        .map(ans => {
            const jsonStr = ans.querySelector("iframe")?.getAttribute("data");
            if (!jsonStr) {
                console.warn("未找到 data 属性或 data 属性为空。");
                return null;
            }
            try {
                const json = JSON.parse(jsonStr);
                return json.type;
            } catch (error) {
                console.warn("解析 JSON 失败:", error);
                return null;
            }
        })
        .filter(type => type !== null);
}

// 获取任务 document
function getAllIframesDocument(arrayAns) {
    return Array.from(arrayAns)
        .map(ans => ans.querySelector("iframe")?.contentWindow?.document)
        .filter(doc => doc !== undefined && doc !== null);
}

// 按任务属性映射处理函数
const handlerMap = {
    ".mp4": videoHandler,
    ".wmv": videoHandler,
    ".avi": videoHandler,
    ".mkv": videoHandler,
    ".pptx": pptxHandler,
    ".pdf": pptxHandler,
    ".ppt": pptxHandler,
    ".mp3": audioHandler,
    ".wav": audioHandler
};

// 按任务属性分配执行函数
function distributeAns(arrayType, arrayDocument) {
    return arrayType.map((type, index) => {
        const handler = handlerMap[type] || ignoreAns;
        return { document: arrayDocument[index], handler };
    });
}

// 处理单个任务
function dealSingleAns(singleAns) {
    if (singleAns && singleAns.handler && typeof singleAns.handler === 'function') {
        singleAns.handler(singleAns.document);
    } else {
        console.warn("无效的任务或处理函数。");
        document.dispatchEvent(dealEvent);
    }
}

/** 执行函数 */
// 直接跳过
function skipChapter() {
	addlog("跳过章节");
	const chapterNext = window.top.document.querySelector("#prevNextFocusNext");
	if (chapterNext) {
			chapterNext.click();
	} else {
			// console.warn("未找到 #prevNextFocusNext 元素。");
	}

	setTimeout(() => {
			const tip = document.querySelector(".maskDiv.jobFinishTip.maskFadeOut");
			if (tip) {
					const tipNextChapter = document.querySelector(".jb_btn.jb_btn_92.fr.fs14.nextChapter");
					tipNextChapter?.click();
			} else {
					// console.warn("未找到完成提示或下一章节按钮。");
			}
			setTimeout(() => {
					initAll();
					// 注意:这里不再立即调用 dealAnsEchelon,而是依靠事件触发
			}, 5000);
	}, 2000);
}

// 忽略任务
function ignoreAns() {
	addlog("无法处理, 忽略任务");
	setTimeout(() => {
			document.dispatchEvent(dealEvent);
	}, 1500);
}

// 处理视频
function videoHandler(idocument) {
	addlog("处理视频任务中...");
	const video = idocument.querySelector("video");
	const videoPlayButton = idocument.querySelector(".vjs-big-play-button");
	const videoDoubleSpeedButton = idocument.querySelector(".vjs-menu-item");

	if (!video) {
			// console.log("未找到视频元素。");
			// document.dispatchEvent(dealEvent);
			return;
	}

	// 将声音调节至0
	video.volume = 0;
	// 播放视频
	videoPlayButton?.click();

	// 设置双倍速播放
	videoDoubleSpeedButton?.click();

	// 监听视频暂停事件并在暂停时继续播放
	const handlePause = () => {
		// console.log("视频暂停,自动恢复播放...");
		videoPlayButton?.click();
		// 如果检测到弹窗,则点击隐藏
		const modalDialog = idocument.querySelector(".vjs-modal-dialog-content");
		const closeButton = idocument.querySelector(".vjs-done-button");
		const ariaHiddenValue = modalDialog.getAttribute("aria-hidden");
		if (modalDialog && closeButton && !ariaHiddenValue) {
			setTimeout(() => {
					closeButton?.click();
					// console.log('模块弹窗已隐藏...')    
			}, 2000);
		}
	};

	video.addEventListener("pause", handlePause);

	// 监听视频播放结束事件,并在结束时触发 dealEvent,并且移除暂停事件监听
	video.addEventListener("ended", () => {
			document.dispatchEvent(dealEvent);
			video.removeEventListener("pause", handlePause);
	}, { once: true });
	// 10秒之后如果还没播放,执行点击播放按钮
	setTimeout(() => {
			if (video.paused && !video.ended) {
				videoPlayButton?.click();
				addlog('由于程序出错未自动播放,现重新模拟点击播放按钮...');
			}
	}, 10000);
	// 同步显示视频进度
	video.addEventListener("timeupdate", function() {
		let currentTime = video.currentTime; // 当前播放时间(以秒为单位)
		let duration = video.duration; // 视频总时长(以秒为单位)
		let progress = (currentTime / duration) * 100; // 计算播放进度百分比
		setVideoProcess(progress.toFixed(2) + '%', currentTime.toFixed(0), duration.toFixed(0)); // 设置视频播放进度显示
	});
}

// 处理 PPT & PDF
function pptxHandler(idocument) {
	  addlog("处理 PPT/PDF 任务中...");
    const iframe = idocument.querySelector("iframe");
    if (!iframe) {
        // console.warn("未找到嵌入的 iframe 元素。");
        document.dispatchEvent(dealEvent);
        return;
    }

    const sDocument = iframe.contentWindow?.document;
    if (!sDocument) {
        // console.warn("无法获取 PPT/PDF 的文档内容。");
        document.dispatchEvent(dealEvent);
        return;
    }

    const finalHeight = sDocument.documentElement.scrollHeight;
    let currentHeight = 0;

    const scrollStep = 400; // 每次滚动的高度
    const scrollInterval = 1000; // 滚动的时间间隔(毫秒)

    const timer = setInterval(() => {
        if (currentHeight >= finalHeight) {
            clearInterval(timer);
            document.dispatchEvent(dealEvent);
            return;
        }
        currentHeight += scrollStep;
        sDocument.defaultView.scrollTo(0, currentHeight);
    }, scrollInterval);
}

// 处理音频
function audioHandler(idocument) {
	  addlog("处理音频任务中...");
    const audio = idocument.querySelector("audio");
    const audioPlayButton = idocument.querySelector(".vjs-play-control.vjs-control.vjs-button");

    if (!audio) {
        console.warn("未找到音频元素。");
        document.dispatchEvent(dealEvent);
        return;
    }

    // 播放音频
    audioPlayButton?.click();

    // 监听音频播放结束事件
    audio.addEventListener("ended", () => {
        document.dispatchEvent(dealEvent);
    }, { once: true });

    // 监听音频暂停事件并在暂停时继续播放
    audio.addEventListener("pause", () => {
        // console.log("音频暂停,自动恢复播放...");
        audioPlayButton?.click();
    });
}

/** 任务梯队顺序处理 */
function dealAnsEchelon(arrayEchelon) {
    const remainingTasks = arrayEchelon.length;
		addlog(`待处理任务数量为: ${remainingTasks}`);

    if (remainingTasks === 0) {
        skipChapter();
        return;
    }

    const nextTask = arrayEchelon.shift();
    try {
        dealSingleAns(nextTask);
    } catch (error) {
        console.error("处理任务时发生错误:", error);
        // 继续处理下一个任务
        dealAnsEchelon(arrayEchelon);
    }
}



// 方法执行入口
(function () {
	// 进入学习通弹出提示
	if(location.pathname == '/base') {
		notify('已进入学习通首页,请进入课程,选择需要学习的课程', 5000)
	}
	// 进入课程弹出提示
	if(location.pathname == '/mooc2-ans/mycourse/stu') {   
		courseName = window.top.document.querySelector('.classDl .colorDeep').getAttribute('title')
		GM_setValue("courseName", courseName);
		notify('已进入课程:' + courseName + ',请选择需要学习的章节', 5000)
	}
	// 初始化显示页面弹窗
	if(location.pathname == '/mycourse/studentstudy') {
		initPopup()  
		// 获取公告数据
		getBoard()  
		// 邮箱操作
		takeEmail() 
	}
	// 获取页面章节节点数据
	getChapterCodeInfo()
	// 获取当前页面章节小节的所有分栏
	getSubChapter()
	// 获取页面url信息
	getURLInfo()

	// 定时打印视频播放进度-定时监听页面内容进行下一步处理
	videoProgressId = setInterval(() => {
		getVideoProgress()
	}, 10000);


		/** 执行代码 */
	setTimeout(() => {
		initAll();
		// 不再直接调用 dealAnsEchelon,这将由 initAll 中的事件触发
	}, 1500);
})();