// ==UserScript== // @name 大橘の学习公社 // @namespace HugeOrange // @version 1.1.0 // @description 学习公社自动看视频、自动刷新、自动跳过防疲劳、自动禁音和倍速播放 // @author HugeOrange // @match https://study.enaea.edu.cn/viewerforccvideo.do* // @match https://study.enaea.edu.cn/circleIndexRedirect.do* // @grant none // @license MIT // @downloadURL none // ==/UserScript== function getQueryVariable(variable) { var query = window.location.search.substring(1); var vars = query.split("&"); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split("="); if (pair[0] == variable) { return pair[1]; } } return (false); } (function () { var url = window.location.pathname // 从localStorage读取保存的播放速度,如果没有则使用默认值4.0 var playbackSpeed = parseFloat(localStorage.getItem('videoPlaybackSpeed') || '4.0'); // 向页面中添加倍速输入框 function addSpeedInput() { // 等待页面加载完成 setTimeout(function() { // 找到"赞"按钮所在的位置 var likeButton = document.getElementById('like'); if (!likeButton) return; // 创建一个新的元素用于放置倍速控制 var speedControlLi = document.createElement('li'); // 使用自定义样式,不使用现有的类名 speedControlLi.id = 'speed-control'; // 自定义样式 speedControlLi.style.marginRight = '10px'; speedControlLi.style.display = 'flex'; speedControlLi.style.alignItems = 'center'; speedControlLi.style.height = '30px'; speedControlLi.style.background = 'none'; // 移除背景 speedControlLi.style.padding = '0 5px'; speedControlLi.style.position = 'relative'; // 创建自定义的倍速控制容器 var speedControlContainer = document.createElement('div'); speedControlContainer.style.display = 'flex'; speedControlContainer.style.alignItems = 'center'; speedControlContainer.style.height = '100%'; speedControlContainer.style.padding = '0 5px'; speedControlContainer.style.backgroundColor = '#f5f5f5'; speedControlContainer.style.borderRadius = '4px'; speedControlContainer.style.border = '1px solid #e0e0e0'; // 创建标签 var speedLabel = document.createElement('span'); speedLabel.textContent = '倍速: '; speedLabel.style.color = '#666'; speedLabel.style.fontSize = '12px'; speedLabel.style.marginRight = '3px'; // 创建输入框 var speedInput = document.createElement('input'); speedInput.type = 'number'; speedInput.min = '0.5'; speedInput.max = '16'; // 增加最大倍速至16 speedInput.step = '0.25'; speedInput.value = playbackSpeed; speedInput.style.width = '40px'; speedInput.style.height = '22px'; speedInput.style.textAlign = 'center'; speedInput.style.border = '1px solid #ccc'; speedInput.style.borderRadius = '3px'; speedInput.style.padding = '0 5px'; speedInput.style.backgroundColor = '#fff'; speedInput.style.verticalAlign = 'middle'; speedInput.style.fontSize = '12px'; speedInput.style.outline = 'none'; // 监听输入变化 speedInput.addEventListener('change', function() { var speed = parseFloat(this.value); // 限制范围在0.5到16之间 if (speed < 0.5) speed = 0.5; if (speed > 16) speed = 16; this.value = speed; playbackSpeed = speed; setVideoSpeed(speed); }); // 组装元素 speedControlContainer.appendChild(speedLabel); speedControlContainer.appendChild(speedInput); speedControlLi.appendChild(speedControlContainer); // 插入到"赞"按钮之前 likeButton.parentNode.insertBefore(speedControlLi, likeButton); }, 2000); // 等待2秒确保页面元素加载完成 } // 设置视频速度并保存到localStorage function setVideoSpeed(speed) { var video = document.getElementsByTagName('video')[0]; if (video) { video.playbackRate = speed; console.log('设置播放速度为: ' + speed); // 保存到localStorage localStorage.setItem('videoPlaybackSpeed', speed); } } // 静音视频 function muteVideo() { var video = document.getElementsByTagName('video')[0]; if (video) { video.muted = true; console.log('视频已静音'); } } //视频播放页 if (url == '/viewerforccvideo.do') { //清理localStorage,以防不给加进度。 localStorage.clear(); localStorage.setItem('videoIsDone', false) // 添加倍速控制 addSpeedInput(); // 防疲劳 setTimeout(() => { let video = document.getElementsByTagName('video')[0]; console.log(video) // 设置播放速度 if (video) { setVideoSpeed(playbackSpeed); // 自动静音 muteVideo(); // 等待视频加载元数据,确保获取的duration有效 if (video.readyState === 0) { // HAVE_NOTHING video.addEventListener('loadedmetadata', jumpToSavedProgress); } else { jumpToSavedProgress(); } } //pause:暂停监听 video.addEventListener('pause', function (e) { console.log('暂停播放') //继续播放 videoPlay() //删除弹窗 let dialog = document.getElementById('rest_tip'); if (dialog) { dialog.remove() } }) // 监听播放事件,确保每次播放都设置正确的速度和静音状态 video.addEventListener('play', function() { setVideoSpeed(playbackSpeed); muteVideo(); // 确保始终保持静音 }); }, 5000) // 根据学习进度跳转到相应位置 function jumpToSavedProgress() { try { // 获取视频元素 let video = document.getElementsByTagName('video')[0]; if (!video || !video.duration) { console.log('视频尚未完全加载,无法获取时长'); return; } // 获取当前视频的学习进度 let progressElements = document.getElementsByClassName('current'); if (progressElements.length < 2 || !progressElements[1].children[0].childNodes[1]) { console.log('无法获取当前视频学习进度元素'); return; } let progressText = progressElements[1].children[0].childNodes[1].innerText; let progressPercent = parseFloat(progressText.replace('%', '')) / 100; // 如果进度为0%或100%,不进行跳转 if (progressPercent === 0 || progressPercent >= 1) { console.log('视频进度为' + (progressPercent === 0 ? '0%,从头开始播放' : '100%,从头开始播放')); return; } // 计算应该跳转到的时间点(向下取整到分钟) let totalSeconds = video.duration; let targetSeconds = Math.floor(totalSeconds * progressPercent); let targetMinutes = Math.floor(targetSeconds / 60); let jumpToSeconds = targetMinutes * 60; // 如果跳转位置太接近视频末尾,稍微往前调整 if (jumpToSeconds > totalSeconds - 30) { jumpToSeconds = Math.max(0, totalSeconds - 60); // 留出至少60秒 } // 执行跳转 video.currentTime = jumpToSeconds; console.log('视频总时长: ' + formatTime(totalSeconds)); console.log('当前学习进度: ' + progressText); console.log('跳转到时间点: ' + formatTime(jumpToSeconds)); // 显示跳转提示 showJumpNotification(progressText, formatTime(jumpToSeconds)); } catch (e) { console.error('跳转到学习进度时出错:', e); } } // 格式化时间为 MM:SS 格式 function formatTime(seconds) { let minutes = Math.floor(seconds / 60); let remainingSeconds = Math.floor(seconds % 60); return minutes + ':' + (remainingSeconds < 10 ? '0' : '') + remainingSeconds; } // 显示跳转提示 function showJumpNotification(progress, timePoint) { // 创建通知元素 let notification = document.createElement('div'); notification.style.position = 'fixed'; notification.style.top = '80px'; notification.style.left = '50%'; notification.style.transform = 'translateX(-50%)'; notification.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; notification.style.color = 'white'; notification.style.padding = '10px 20px'; notification.style.borderRadius = '5px'; notification.style.zIndex = '9999'; notification.style.fontFamily = 'Arial, sans-serif'; notification.style.fontSize = '14px'; notification.textContent = `已跳转到上次学习位置 (${progress} → ${timePoint})`; // 添加到页面 document.body.appendChild(notification); // 3秒后移除 setTimeout(() => { if (notification && notification.parentNode) { notification.parentNode.removeChild(notification); } }, 3000); } //五秒后关闭声音 function Music_No() { setTimeout(function () { document.getElementsByClassName("xgplayer-icon-muted")[0].click() }, 5000) } //点击未完成的视频进行播放 function rePlay() { setTimeout(function () { if (document.getElementsByClassName("cvtb-MCK-CsCt-studyProgress")[0].innerHTML == '100%') { for (var i = 1; i < document.getElementsByClassName("cvtb-MCK-CsCt-studyProgress") .length; i++) { if (document.getElementsByClassName("cvtb-MCK-CsCt-studyProgress")[i].innerHTML != '100%') { document.getElementsByClassName("cvtb-MCK-CsCt-studyProgress")[i].click() break } } } }, 2000) } //隔五秒循环执行 setInterval(function () { //最后一个视频的index index = document.getElementsByClassName("cvtb-MCK-CsCt-studyProgress").length - 1 //查看当前课前是否完成 if (document.getElementsByClassName('current')[1].children[0].childNodes[1].innerText == '100%') { //如果最后一个视频完成了就代表全部视频都完成了 if (document.getElementsByClassName("cvtb-MCK-CsCt-studyProgress")[index].innerHTML == '100%') { //关闭网站 localStorage.setItem('videoIsDone', true) window.close() } else { //否则播放下一个未完成的视频 rePlay() } } else { console.log("正在观看:" + document.getElementsByClassName('current')[1].children[0] .getElementsByClassName("cvtb-text-ellipsis")[0].innerHTML) } //如果出现异常就刷新网页 if (document.getElementsByClassName("dialog-content")[0] != undefined) { if (document.getElementsByClassName("dialog-content")[0].innerText == '学时记录出现异常请检查网络') { location.reload() } } }, 5000) setInterval(function () { //播放视频 videoPlay() // 每隔一段时间检查并确保设置正确的播放速度和静音状态 let video = document.getElementsByTagName('video')[0]; if (video) { if (video.playbackRate !== playbackSpeed) { setVideoSpeed(playbackSpeed); } if (!video.muted) { muteVideo(); // 确保始终保持静音 } } }, 1000) rePlay() Music_No() } //课程学习页 if (url == '/circleIndexRedirect.do') { function playVideo_2() { //切换到未完成的课程tab document.getElementsByClassName('customcur-tab-text')[1].click() //检测是否完成视频 if (localStorage.getItem('videoIsDone') == 'true') { location.reload() } else { console.log("观看视频:", document.getElementsByClassName('course-title')[0].innerText) } } //首次进入,只执行一次 function playVideo_1() { //切换到未完成的课程tab document.getElementsByClassName('customcur-tab-text')[1].click() //检测是否完成视频 if (localStorage.getItem('videoIsDone') == null) { alert('切换到课程学习页自动刷视频,请确保视频可以正常播放后在挂机,点击确定开始运行') } //循环 setTimeout(function () { //点击第一个开始学习 document.getElementsByClassName('golearn')[0].click() }, 1000) } //判断是否为试卷页面 if (getQueryVariable('type') == 'exam') { var formDdata = { "circleId": cid, "syllabusId": sid, }; $.ajax({ type: "GET", url: 'circleIndex.do?action=getMyClass', data: formDdata, async: true, beforeSend: function () { console.log('请等待...'); }, complete: function (XMLHttpRequest, status, errorThrown) { console.log('获取完成...'); }, success: function (data) { console.log('获取成功...'); console.log(data) var dataJson = eval('(' + data + ')'); console.log(dataJson.result['list'][0].id) setTimeout(function () { var styleMap = { "width": "100px", display: "inline-block", "background-color": "red", cursor: "pointer", "user-select": "none", "border-radius": "4px", color: "#fff", "font-size": "10px", "line-height": "30px", "margin": "0px 30px", }; var btn = document.createElement("a"); btn.innerHTML = "提前查看试卷"; for (let i in styleMap) { btn.style[i] = styleMap[i]; } btn.href = '/myExamAndTestRedirect.do?action=toSeeExamResult&ct=&examId=' + dataJson.result['list'][0].id var toolbox = document.getElementsByClassName("item-title")[0] toolbox.appendChild(btn); alert('请点击试卷标题旁的按钮查看试卷') }, 100) }, error: function (XMLHttpRequest, textStatus, errorThrown) { console.log('获取失败...'); } }); } else { setTimeout(function () { playVideo_1() }, 2000) setInterval(function () { playVideo_2() }, 5000) setTimeout(function () { localStorage.setItem('videoIsDone', false) location.reload() }, 1000 * 60 * 10) } } })();