Greasy Fork

国家中小学智慧教育平台刷课脚本(16倍速,自动答题,自动切换列表中的视频,后台播放,学时不更新的解决方法看下面)

16倍速,自动答题,自动切换列表中的视频,后台播放,学时不更新的解决方法看下面

目前为 2023-07-14 提交的版本。查看 最新版本

// ==UserScript==
// @name         国家中小学智慧教育平台刷课脚本(16倍速,自动答题,自动切换列表中的视频,后台播放,学时不更新的解决方法看下面)
// @namespace    http://tampermonkey.net/
// @version      3.0.4
// @license      CC BY-NC-SA
// @description  16倍速,自动答题,自动切换列表中的视频,后台播放,学时不更新的解决方法看下面
// @author       HGGshiwo
// @match        https://*.zxx.edu.cn/teacherTraining/courseDetail*
// @match        https://basic.smartedu.cn/teacherTraining/courseDetail*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

(function () {
    ("use strict");
    const courses = [
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=58979766-6cb2-43f8-8b6c-68336f8824df&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=d0d1b11d-5d55-4014-9b80-72841dbd55bf', resNo: 2, groupNo: 0 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=10c4b18a-333a-48d2-a6ea-4a3524ea8e78&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=abcadb14-3ce2-4d3f-a106-daf9cdfbd3b2', resNo: 0, groupNo: 0 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=f47898b2-2138-4600-b08d-68856b1c7cb7&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=cd689fe2-b474-4fdf-96a5-9cc51eaa91b7', resNo: 1, groupNo: -1 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=785b41e5-aedb-4adc-9e64-7e1cee9b42b1&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=163cc431-2678-432b-884d-0a4d9f8e9362', resNo: 0, groupNo: 2 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=785b41e5-aedb-4adc-9e64-7e1cee9b42b1&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=163cc431-2678-432b-884d-0a4d9f8e9362', resNo: 0, groupNo: 2 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=c016de39-7291-466d-be35-045f9c2ffe44&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=bf2b3e72-3c96-472d-aa1c-4c4f809ff0a8', resNo: 1, groupNo: 0 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=a326cae7-067b-44b6-910c-c9a4747bb02e&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=e0a0b1f4-ce31-4bef-bf6b-09e8fe8dcbac', resNo: 1, groupNo: 0 },
      { urls: 'https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=5e2191f1-b7a2-4d46-b833-de939fc06696&tag=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&channelId=&libraryId=bb042e69-9a11-49a1-af22-0c3fab2e92b9&breadcrumb=2023%E5%B9%B4%E2%80%9C%E6%9A%91%E6%9C%9F%E6%95%99%E5%B8%88%E7%A0%94%E4%BF%AE%E2%80%9D%E4%B8%93%E9%A2%98&resourceId=d79cbd29-c2df-407e-bd2d-9d89f984d0bc', resNo: -1, groupNo: -1 }
    ]
    // function
    const changInputValue = (inputDom, newText) => {
      if (!inputDom) {
        return;
      }
      let lastValue = inputDom.value;
      inputDom.value = newText;
      let event = new Event("input", { bubbles: true });
      event.simulated = true;
      let tracker = inputDom._valueTracker;
      if (tracker) {
        tracker.setValue(lastValue);
      }
      inputDom.dispatchEvent(event);
    };
  
    function findLastIndex(array, predicate) {
      // 先将数组反转
      const reversedArray = array.slice().reverse();
      // 使用findIndex找到满足条件的元素的索引
      const index = reversedArray.findIndex(predicate);
      if (index === -1) {
        return -1; // 若未找到,则直接返回-1
      }
      // 计算满足条件的元素在原数组中的索引
      const originalIndex = array.length - 1 - index;
      return originalIndex;
    }
  
    const State = {
      LoadPage: "loadPage",
      GetActive: "getActive",
      SwitchSource: "switchSource",
      PlayVideo: "playVideo",
      HandlePlayRes: "handlePlayRes",
      WaitPlay: "waitPlay",
      SwitchActive: "switchActive",
      SwitchFirst: "switchFirst",
      TaskEnd: "taskEnd",
    }
  
    var state = State.LoadPage;
    var groups = undefined;
    var groupNo = undefined;
    var resItems = undefined;
    var resNo = undefined;
    var videoErr = undefined;
  
    const func_table = {
      loadPage: () => {
        var video = document.querySelector("video");
        var resItems = document.querySelector(".resource-item");
        if (!!video && !!resItems) {
          return State.GetActive
        }
        else {
          console.log(666, "等待视频加载")
          return State.LoadPage
        }
      },
      getActive: () => {
        groups = document.getElementsByClassName("fish-collapse-item");
        //寻找最后一个打开的group(子group可能打开多个)
        //适配chrome版本低于97, firefox版本低于108的用户
        groupNo = findLastIndex([...groups], (item) => {
          return item.className.includes("active");
        })
  
        var base = groupNo === -1 ? document : groups[groupNo];
        resItems = base.getElementsByClassName("resource-item");
        resNo = [...resItems].findIndex((item) => {
          return item.className.includes("active");
        });
        return State.SwitchSource
      },
      switchSource: () => {
        //视频修改为标清 zxj663建议添加
        let sped = document.querySelector(
          "div.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button.vjs-resolution-button > span"
        );
        if (sped && sped.innerText != "标清") {
          document
            .querySelector(
              "div.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button.vjs-resolution-button > div > ul > li:nth-child(2) > span.vjs-menu-item-text"
            )
            .click();
        }
        return State.PlayVideo
      },
      playVideo: () => {
        let icons = resItems[resNo].getElementsByClassName("iconfont");
        if (icons[1] && icons[1].className.includes("icon_checkbox_fill")) {
          console.log(666, `第${groupNo + 1}组, 第${resNo + 1}个视频已经观看`);
          return State.SwitchActive
        }
  
        console.log(666, `开始观看: 第${resNo + 1}个视频,第${groupNo + 1}组`);
        var video = document.getElementsByTagName("video")[0];
        video.muted = true;
        video.play().then(() => {
          videoErr = false
        }).catch((err) => {
          console.log(666, err);
          videoErr = true
        });
        renderMenu()
        video.playbackRate = rateMenu[active].value;
        video.addEventListener("pause", () => state = State.PlayVideo, false)
        video.addEventListener("ended", () => state = State.SwitchActive, false)
        return State.HandlePlayRes
      },
      handlePlayRes: () => {
        //处理播放的结果
        return videoErr === undefined ? State.HandlePlayRes : videoErr ? State.PlayVideo : State.WaitPlay
      },
      waitPlay: () => { return state },
      switchActive: () => {
        //从列表中查找当前的课程
        const index = courses.findIndex(course => course.urls === window.location.href)
        if (index !== -1 && courses[index].resNo === resNo && courses[index].groupNo === groupNo && index + 1 < courses.length) {
          console.log(666, "进入下一个学习页面");
          window.open(courses[index + 1].urls, "_self");
        }
  
        //如果没看完当前组,则观看当前组的下一个视频
        if (resNo + 1 != resItems.length) {
          resNo += 1
          resItems[resNo].click();
          console.log(666, `点击当前组的下一个视频`);
          return State.SwitchSource;
        }
  
        //如果看完了当前组,没看完当前页面,则看下一个页面
        if (groupNo + 1 != groups.length) {
          console.log(666, `点击下一组的第一个视频`);
          groupNo += 1
          document.getElementsByClassName("fish-collapse-header")[groupNo].click();
          return State.SwitchFirst
        }
        //如果都看完了
        return State.TaskEnd
      },
      switchFirst: () => {
        resItems = groups[groupNo].getElementsByClassName("resource-item");
        resNo = 0
        resItems[resNo].click();
        return State.SwitchSource
      },
      taskEnd: () => {
        return State.TaskEnd;
      }
    }
  
    const setVideoHandler = () => {
      setInterval(() => {
        try {
          var popup = false;
          [".nqti-option", ".index-module_markerExercise_KM5bU .fish-btn", ".fish-modal-confirm-btns .fish-btn"].forEach(selector => {
            let dom = document.querySelector(selector)
            if (!!dom) {
              popup = true
              dom.click()
            }
          })
          //增加填空题支持
          var inputForm = document.querySelector(".index-module_box_blt8G");
          if (!!inputForm) {
            popup = true
            changInputValue(inputForm.getElementsByTagName("input")[1], "&nbsp;");
          }
          if (!popup) {
            state = func_table[state]()
            console.log(666, `${state}已经完成!`)
          }
          else {
            console.log(666, "处理弹窗")
          }
        }
        catch (err) {
          console.log(666, `${state}: ${err}`)
        }
      }, 1000)
    }
  
    //修改播放速度
    const changeRate = (rate, index) => {
      localStorage.setItem("active", `${index}`)
      active = index
      document.querySelector(".vjs-playback-rate-value").innerHTML = rateMenu[index].title
      document.getElementsByTagName("video")[0].playbackRate = rate
      return false
    }
  
    //修改速度菜单
    const renderMenu = () => {
      document.querySelector(".vjs-playback-rate .vjs-menu-content").innerHTML =
        rateMenu.map((rate, index) =>
          `<li class="vjs-menu-item" tabindex="-1" role="menuitemradio" aria-disabled="false" aria-checked="${index == active}">
          <span class="vjs-menu-item-text">${rate.title}</span>
          <span class="vjs-control-text" aria-live="polite"></span>
        </li>`
        ).join(" ")
      const doms = document.querySelectorAll(".vjs-playback-rate .vjs-menu-content .vjs-menu-item")
      rateMenu.forEach((rate, index) => {
        doms[index].addEventListener("click", () => changeRate(rate.value, index), false)
      })
  
      //显示速度控制菜单
      const rateButtons = document.getElementsByClassName("vjs-playback-rate vjs-menu-button vjs-menu-button-popup vjs-control vjs-button vjs-hidden")
      if (rateButtons.length > 0) {
        rateButtons[0].classList.remove("vjs-hidden")
        document.querySelector(".vjs-playback-rate-value").innerHTML = rateMenu[active].title
      }
    }
  
    //获取速度
    let activeStr = localStorage.getItem("active")
    const rateMenu = [{ title: "1x", value: 1 }, { title: "4x", value: 4 }, { title: "8x", value: 8 }, { title: "12x", value: 12 }, { title: "16x", value: 16 }]
    let active = activeStr === null ? rateMenu.length - 1 : parseInt(activeStr)
  
    //下面开始运行脚本
    console.log(666, "开始执行脚本")
    setVideoHandler();
    setPopupHandler();
  })();