国开大学网课一网一##视频自动化播放,快进,解放你的双手🔥支持最高16倍倍速播放,播完后自动播放下一集
// ==UserScript==// @name 国开大学视频网课,一网一##自动化脚本// @namespace https://github.com/ijinfeng/jp_video// @version 1.1// @description 国开大学网课一网一##视频自动化播放,快进,解放你的双手🔥支持最高16倍倍速播放,播完后自动播放下一集// @author jinfeng// @match *://menhu.pt.ouchn.cn/*// @match *://lms.ouchn.cn/course/*// @license GPL License// @icon https://www.google.com/s2/favicons?sz=64&domain=baidu.// @grant GM_addStyle// @grant unsafeWindow// ==/UserScript==(function () {// 通过脚本点击开始播放let startPlay = false;// 当前的播放对象(视频专题)let currentPlayObj = null;// 最高播放速率const kHighestRate = 16.0;function readTitleFromItem(item) {if (!item) return null;let title = item.querySelector('.activity-title')let span = title.querySelector('span')let distitle = span.getAttribute('original-title')return distitle;}function isVideo(item) {const icon = item.getElementsByClassName('full-screen-activity-icon')[0];const ot = icon.getAttribute('original-title');// console.log(`- ${ot}`);return ot === '音视频教材';}/// 寻找当前打开的专题/// @param selected: 播放选中,否则播放最新的function findCurrentOpenedTopic(selected) {let currentItem = null;let nextItem = null;let targetIndex = undefined;let items = document.getElementsByClassName('activity ng-scope') // 选中:activity ng-scope active// let items = document.getElementsByClassName('activity-menu-item')let videoItems = []for (const item of items) {if (isVideo(item) == false) {continue;}videoItems.push(item)}if (videoItems.length == 0) {console.log("Can't find any video item, you should select a special topic first!");return;}let findedTarget = false;for (let index = 0; index < videoItems.length; index++) {const item = videoItems[index];currentItem = nextItem;nextItem = item;const isActive = item.classList.contains('active');let distitle = readTitleFromItem(item);console.log(`+ ${distitle}|${isActive}`);if (distitle) {// find lockif (selected) {if (isActive) {findedTarget = true;targetIndex = index;break;}} else {let lock = item.querySelector('.is-locked');if (lock) {findedTarget = true;targetIndex = index - 1;break}}}}if (findedTarget) {if (targetIndex == undefined) {targetIndex = videoItems.length - 1;}currentItem = videoItems[targetIndex];if (targetIndex < videoItems.length - 1) {nextItem = videoItems[targetIndex+1];}console.log(`💪 Successfully find the video at index: ${targetIndex} that you want to play!`);console.log(`Current title of video is ${readTitleFromItem(currentItem)}`);} else {console.log('Failed find the video in curren topic scope.');return null;}if (targetIndex >= videoItems.length) {console.log('There are no more videos to play!');} else {console.log(`The next video is ${readTitleFromItem(nextItem)}`);}return {current: currentItem,items: videoItems,index: targetIndex};}function logCurrentPlayObj() {if (!currentPlayObj) {console.log('👀 Current video topic is empty.');return;}console.log(`🖨️ Video items count: ${currentPlayObj.items.length}, index: ${currentPlayObj.index}, curTtitle: ${readTitleFromItem(currentPlayObj.current)}`);}function selectedTargetItemAt(index) {if (!index || !currentPlayObj) {console.log('Target item is empty!');return;}if (index > currentPlayObj.items.length - 1) {console.log(`The target index: ${index} out of bounds: {0, ${currentPlayObj.items.length}}`);return;}currentPlayObj.index = index;const item = currentPlayObj.items[index];currentPlayObj.current = item;let clickEvent = new Event('click');item.querySelector('.activity-menu-item').dispatchEvent(clickEvent);console.log(`Auto select video item named: ${readTitleFromItem(item)}`);}function playEnded() {console.log(`play end - [${currentPlayObj.index}, ${currentPlayObj.items.length}]`);if (isLastOne(currentPlayObj)) {console.log('There are no more videos in playlist.');alert('There are no more videos in playlist.');startPlay = false;return;}// hoverlet nextItem = getNextPlayItem(currentPlayObj);console.log(`Ready to find next video named: ${readTitleFromItem(nextItem)}`);// 先检查下是否存在锁const isExsitLock = exsitLock(nextItem);if (isExsitLock) {console.log('Find a lock in this video item, try remove it.');tryRemoveLock(nextItem);console.log('🚌 Wait 1 second to continue...');let t = setTimeout(() => {console.log('OK, start the next step!');selectedTargetItemAt(currentPlayObj.index + 1);startPlayVideo();logCurrentPlayObj();clearTimeout(t);}, 1000);} else {selectedTargetItemAt(currentPlayObj.index + 1);startPlayVideo();logCurrentPlayObj();}}function exsitLock(item) {let lock = item.querySelector('.is-locked');return lock != undefined || lock != null;}function startPlayVideo() {if (!currentPlayObj) return;isElementLoaded('.mvp-toggle-play').then(() => {let playBtn = document.querySelector(".mvp-toggle-play");let clickEvent = new Event('click');playBtn.dispatchEvent(clickEvent);isElementLoaded('video').then(() => {let video = document.querySelector('video');video.currentTime = 0;video.playbackRate = currentPlayObj.playrate;video.play();video.removeEventListener('ended', playEnded);video.addEventListener('ended', playEnded);});});}// 继续播放,这种情况一般是失去焦点导致的function r###mePlayer() {console.log('r###me player');isElementLoaded('.mvp-toggle-play').then(() => {isElementLoaded('video').then(() => {let video = document.querySelector('video');console.log(`Find video player is paused: ${video.paused}, rate: ${video.playbackRate}`);if (!video.paused) {return;}let playBtn = document.querySelector(".mvp-toggle-play");let clickEvent = new Event('click');playBtn.dispatchEvent(clickEvent);if (!video.paused) {return;}video.play();});});}// Element loadedconst isElementLoaded = async selector => {while (document.querySelector(selector) === null) {await new Promise(resolve => requestAnimationFrame(resolve))}return document.querySelector(selector);};function isLastOne(playObj) {return playObj.index >= playObj.items.length - 1;}function getNextPlayItem(playObj) {if (!playObj) return null;if (isLastOne(playObj)) return null;return playObj.items[playObj.index + 1];}function setNextPlayItemReady(playObj) {if (!playObj) return null;if (isLastOne(playObj)) return null;playObj.index++;playObj.current = playObj.items[playObj.index];}function tryRemoveLock(item) {let locks = item.querySelectorAll('.right');for (const lock of locks) {if (lock) {lock.innerHTML = "";}}}/// 用户交互界面function createUserInterface() {let box = document.createElement('div');box.className = 'jp-box';box.innerHTML = `<p class="jp-title">国开网课脚本</p><label class="jp-rate-label">播放速率:<input type="text" value="16" class="jp-rate-input"></label><button class="jp-play-btn" id="jp-play1">从最新项开始播放</button><button class="jp-play-btn" id="jp-play2">从选中项开始播放</button><button class="jp-play-btn" id="jp-play-stop">停止运行脚本</button>`document.body.appendChild(box);setUserCss()const playBtn1 = document.querySelector('#jp-play1');playBtn1.onclick = userClickStartPlayByNew;const playBtn2 = document.querySelector('#jp-play2');playBtn2.onclick = userClickStartPlayBySelected;const playStopBtn = document.querySelector('#jp-play-stop');playStopBtn.onclick = userClickStopRunScript;}function setUserCss() {let css = `.jp-box {position: fixed;right: 30px;bottom: 100px;width: 140px;height: 160px;display: flex;flex-direction: column;align-items: center !important;justify-content: center !important;background-color: #f4f5f6;border-radius: 8px;font-size: 12px !important;text-align: center !important;border: 1px solid #999;overflow: hidden;z-index: 10;}.jp-title {color: #333333;line-height: 30px !important;background-color: antiquewhite;width: 100%;margin: 0;text-align: center;}.jp-rate-label {margin-top: 6px;margin-bottom: 6px;}.jp-rate-input {width: 40px !important;height: 25px !important;display: inline-block !important;background-color: rgb(185, 226, 226) !important;}.jp-play-btn {font-size: 12px !important;height: 20px;line-height: 20px;padding: 0 16px;margin-bottom: 5px;}`GM_addStyle(css)}function userClickStartPlayByNew() {const state = userReadyClickPlayVideo();isElementLoaded('video').then(() => {console.log('Video is loaded!');let playObj = findCurrentOpenedTopic();if (!playObj) {userClickStopRunScript();return;}currentPlayObj = playObj;playObj.playrate = state.rate;selectedTargetItemAt(playObj.index);startPlayVideo();})}function userClickStartPlayBySelected() {const state = userReadyClickPlayVideo();isElementLoaded('video').then(() => {console.log('Video is loaded!');let playObj = findCurrentOpenedTopic(true);if (!playObj) {userClickStopRunScript();return;}currentPlayObj = playObj;playObj.playrate = state.rate;selectedTargetItemAt(playObj.index);startPlayVideo();})}function userReadyClickPlayVideo() {startPlay = true;let rate = document.querySelector('.jp-rate-input').value;if (rate > kHighestRate) {rate = kHighestRate;console.log(`The highest playback rate is ${kHighestRate}`);}console.log(`On click play video with playrate: ${rate}`);listenBlur();return {rate: rate};}function userClickStopRunScript() {console.log('Stop run jp_script');startPlay = false;isElementLoaded('video').then(() => {let video = document.querySelector('video');video.removeEventListener('ended', playEnded);});}function listenBlur() {console.log('Start listen focus/blur events');unsafeWindow.onblur = function () {if (startPlay) {r###mePlayer();}}}console.log('Welcome to use ef_video script!');// 创建用户界面createUserInterface();})();