// ==UserScript== // @name 🚀云班课一键完成所有资源(AI修复版)🚀 // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 修复由于蓝墨云班课页面更新导致的按钮消失和统计崩溃问题 // @author Key77 ( With Gemini3Pro ) Handsomedog // @match https://www.mosoteach.cn/web*/index.php?c=res&m=index&clazz_course_id=* // @icon // @grant GM_addStyle // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/573913/%F0%9F%9A%80%E4%BA%91%E7%8F%AD%E8%AF%BE%E4%B8%80%E9%94%AE%E5%AE%8C%E6%88%90%E6%89%80%E6%9C%89%E8%B5%84%E6%BA%90%28AI%E4%BF%AE%E5%A4%8D%E7%89%88%29%F0%9F%9A%80.user.js // @updateURL https://update.greasyfork.icu/scripts/573913/%F0%9F%9A%80%E4%BA%91%E7%8F%AD%E8%AF%BE%E4%B8%80%E9%94%AE%E5%AE%8C%E6%88%90%E6%89%80%E6%9C%89%E8%B5%84%E6%BA%90%28AI%E4%BF%AE%E5%A4%8D%E7%89%88%29%F0%9F%9A%80.meta.js // ==/UserScript== (function () { 'use strict'; // 1. 获取课程ID var courseIdElem = document.getElementsByName("clazz_course_id")[0]; if (!courseIdElem) return; // 如果不在资源页面则安全退出 var courseId = courseIdElem.value; var pendingTasks = []; // 待完成的任务列表 // 2. 遍历所有资源,动态、健壮地提取数据 $('div[data-mime]').each(function() { var $res = $(this); var type = $res.attr("data-mime"); var value = $res.attr("data-value"); // 查找当前资源特有的完成状态(拖拽状态) var $finishSpan = $res.find('span[data-is-drag]').first(); var finish = $finishSpan.attr("data-is-drag"); // 如果状态为 N (未完成),则加入任务队列 if (finish === 'N') { var task = { type: type, value: value }; if (type === 'video') { // 使用正则提取视频时间,避免以前脆弱的下标定位 (children[4]) var boxText = $res.find('.create-box.manual-order-hide-part').text(); var timeMatch = boxText.match(/(\d{2}):(\d{2}):(\d{2})/); if (timeMatch) { var hour = Number(timeMatch[1]); var minute = Number(timeMatch[2]); var second = Number(timeMatch[3]); task.alltime = (hour * 60 * 60) + (minute * 60) + second; } else { task.alltime = 300; // 匹配不到时间时,默认给5分钟容错时长 } } pendingTasks.push(task); } }); var leaveres = pendingTasks.length; var deepen = 0; var nowdeep = 0; // 3. 插入按钮 (增加 z-index 确保显示在最上层) $("#cc-main").append('
一键完成所有资源
'); GM_addStyle(".progress {position: absolute; right: 20px; top: 75px; width: 180px; height: 25px; background: #e5e5e5; border-radius: 4px; overflow: hidden; cursor: pointer; z-index: 9999; box-shadow: 0 2px 5px rgba(0,0,0,0.2);}"); GM_addStyle(".progressBar { width: 180px; height: 100%; display: flex; justify-content: center; align-items: center; background: cornflowerblue; background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-size: 40px 40px; transition: width .6s ease; border-radius: 4px; animation: progressBar 2s linear infinite;}"); GM_addStyle(".progressBar-value { font-size: 13px; font-weight: bold; color: white; margin-right: 0px; text-shadow: 1px 1px 2px rgba(0,0,0,0.3);}"); GM_addStyle("@keyframes progressBar { from { background-position: 40px 0; } to { background-position: 0 0; }}"); var progressBar = document.getElementsByClassName("progressBar")[0]; var progressBarValue = document.getElementsByClassName("progressBar-value")[0]; if (leaveres === 0) { progressBarValue.innerHTML = `很厉害哦,都完成啦!!!`; progressBar.style.cursor = "default"; } else { progressBarValue.innerHTML = `剩余 ${leaveres} 个任务 (点击开始)`; } // 4. 点击事件与请求派发 $(".progress").click(function () { if (leaveres > 0) { deepen = (100 / leaveres).toFixed(1); progressBarValue.innerHTML = `进度:0%`; progressBar.style.width = '80px'; progressBar.style.justifyContent = 'flex-end'; progressBarValue.style.marginRight = '5px'; } else { return; } // 错峰发送请求,防止同一时间并发过高被服务器拦截 pendingTasks.forEach(function(task, index) { setTimeout(function() { if (task.type === 'video') { getVideo(task.value, task.alltime); } else { getResource(task.value); } }, index * 400); // 每个请求间隔 400 毫秒 }); }); function updateProgress() { leaveres -= 1; if (leaveres <= 0) { progressBarValue.innerHTML = `进度:100%`; progressBar.style.width = '180px'; setTimeout(() => location.reload(), 1000); // 跑完后延迟1秒刷新页面 } else { nowdeep = getNowDeep(nowdeep); progressBar.style.width = Number(nowdeep) + 80 + 'px'; progressBarValue.innerHTML = `进度:${nowdeep}%`; } } function getResource(value) { $.ajax({ type: 'head', url: 'index.php?c=res&m=online_preview&clazz_course_id=' + courseId + '&file_id=' + value, complete: function () { updateProgress(); } }); } function getVideo(value, alltime) { $.ajax({ type: 'post', dataType: 'json', url: 'index.php?c=res&m=save_watch_to', data: { clazz_course_id: courseId, res_id: value, watch_to: alltime, duration: alltime, current_watch_to: 0 }, success: function () { updateProgress(); }, error: function () { console.log("视频保存失败, ID: " + value); updateProgress(); // 即使个别失败也推进进度条,避免卡死 } }); } function getNowDeep(nowdeep) { let nextdeep = (Number(nowdeep) + Number(deepen)).toFixed(1); if (nextdeep >= 100 - deepen) { nextdeep = 100; } else if (nextdeep - Math.floor(nextdeep) >= 0.5) { nextdeep = Math.ceil(nextdeep); } return nextdeep; } })();