// ==UserScript==
// @name 2025国家智慧教育平台寒假研修,可秒刷视频!适合中小学(基础教育、师范生免试认定) | 职业教育 | 高等教育
// @namespace http://tampermonkey.net/zzzzzzys_国家中小学
// @version 1.0.5
// @description 适用2025国家智慧教育平台寒假研修.中小学课程,可快速通过视频学习。即秒刷!| 职业教育/高等教育 可自动挂机。欢迎加入QQ交流群,及时获取最新消息!请勿随意二次发布代码,尊重原创!
// @author zzzzzzys
// @match https://basic.smartedu.cn/*
// @match https://core.teacher.vocational.smartedu.cn/*
// @match https://test3.ykt.eduyun.cn/*
// @icon https://basic.smartedu.cn/favicon.ico
// @require https://fastly.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js
// @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css
// @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js
// @connect basic.smartedu.cn
// @connect x-study-record-api.ykt.eduyun.cn
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_info
// @grant GM_addStyle
// @run-at document-end
// @license GPL-3.0-or-later
// @downloadURL none
// ==/UserScript==
/*****************************
* 盗版可耻
* 请尊重原创劳动成果!
* 作者:zzzzzzys
* https://cn-greasyfork.org/zh-CN/users/1176747-zzzzzzys
* 搬运可耻
****************************/
(function () {
'use strict';
const qqGroup = [
{customName:"群1",id: "570337037", link: "https://qm.qq.com/q/rDCbvTiV9K", isFull: true,priority:0},
{customName:"群2",id: "618010974", link: "https://qm.qq.com/q/h854sxDvKa", isFull: true,priority:2},
{customName:"群3",id: "1003884618", link: "https://qm.qq.com/q/kRcyAunAic", isFull: true,priority:1},
{customName:"群4",id: "821240605", link: "https://qm.qq.com/q/z1ogtdhyGA", isFull: true,priority:2},
{customName:"群5",id: "1013973135", link: "https://qm.qq.com/q/EpXA5Ar3vG", isFull: true,priority:2},
{customName:"交流学习群(禁广告,只交流学习)",id: "978762026", link: "https://qm.qq.com/q/aUTUVmKYQE", isFull: false,priority:0},
]
let qqUrl = "https://qm.qq.com/q/rDCbvTiV9K"
let qqNum = "570337037"
let qqNum2 = "618010974"
let qqUrl2 = "https://qm.qq.com/q/h854sxDvKa"
let biliUrl = "https://b23.tv/x5pFcB0"
// 保存原生 XMLHttpRequest 的引用
const originalXHR = unsafeWindow.XMLHttpRequest;
let fullDatas=null
// 重写 XMLHttpRequest
unsafeWindow.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
const originalSend = xhr.send;
// 重写 open 方法,记录请求信息
xhr.open = function(method, url) {
this._method = method;
this._url = url;
return originalOpen.apply(this, arguments);
};
// 重写 send 方法,监听响应
xhr.send = function(body) {
this.addEventListener('readystatechange', function() {
if(this._url.includes("fulls.json")){
if (this.readyState === 4) { // 请求完成
console.log(body)
console.log('捕获到 XHR 请求结果:', {
url: this._url,
method: this._method,
status: this.status,
response: this.response
});
fullDatas=JSON.parse(this.response);
}
}
});
return originalSend.apply(this, arguments);
};
return xhr;
};
// 预处理群组数据
const renderQQGroups = () => {
try {
const activeGroups = qqGroup
.filter(group => {
// 添加数据校验
if (!group.customName || !group.id) {
console.warn('Invalid group:', group);
return false;
}
return !group.isFull;
})
.sort((a, b) => a.priority - b.priority);
// 添加空状态提示
if (activeGroups.length === 0) {
return `
所有群组已开放,欢迎直接加入
`;
}
const title=`
教师交流群(请优先选择未满群加入)
获取实时支持 | 最新功能优先体验
`
let content= title + activeGroups.map(group => `
🎯 点击加入${group.customName}:${group.id}
`).join('');
return `
${content}
`
} catch (error) {
console.error('QQ群渲染错误:', error);
return ''; // 静默失败
}
};
const groupHtml = `
`
let requestObj = {
fullsData: {
url: "https://s-file-2.ykt.cbern.com.cn/teach/s_course/v2/activity_sets/3efdb592-138e-4854-8964-5e10f6011f33/fulls.json",
method: "GET",
},
resourceLearningPositions: {
url: "https://x-study-record-api.ykt.eduyun.cn/v1/resource_learning_positions/",
method: "PUT"
},
/* 职业教育 | 高等教育 */
progress: {
url: "https://core.teacher.vocational.smartedu.cn/p/course/services/member/study/progress",
method: "POST",
}
}
/********************************************************
* 职业教育/高等教育
*******************************************************/
const SWAL_CONFIG = {
title: '课程进度控制',
html: `
🎯
老师您好,点击开始按钮,开始减负之旅
脚本会自动学习当前页所有视频,您可安心休息片刻
准备就绪
${renderQQGroups()}
By YoungthZou. 盗码可耻! zzzzzzys
`,
showConfirmButton: false,
allowOutsideClick: false,
allowEscapeKey: false,
width: 600,
willOpen: () => {
document.querySelector('.swal2-close').remove();
}
};
// 状态管理
let currentProgress = 60;
let isRunning = false;
let timerId = null;
let swalInstance = null;
let totalTime = 1000;
let checkInterval = null
// 工具函数
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
};
const updateUI = (progress, status) => {
if (!swalInstance) return;
// 更新进度条
const progressBar = swalInstance.querySelector('#swalProgressBar');
const percent = (progress / totalTime * 100).toFixed(1);
progressBar.style.width = `${Math.min(parseFloat(percent), 100)}%`;
// 更新文本显示
swalInstance.querySelector('#currentProgress').textContent = formatTime(progress);
swalInstance.querySelector('#totalTime').textContent = formatTime(totalTime);
swalInstance.querySelector('#needTime').textContent = formatTime(parseInt(((totalTime - progress) / 3).toFixed(0)));
// 更新状态消息
const statusEl = swalInstance.querySelector('#statusMessage');
statusEl.textContent = {
loading: '🔄 正在同步进度...',
success: '✅ 同步成功,stand by...',
error: '❌ 同步失败(长时间失败,请反馈)',
idle: '⏸ 已暂停',
finished: '✅已学完,跳过...',
finishAll: '已全部学完,请手动刷新,给个好评吧~',
next: "🔄 此视频已学完,准备学习下一个..."
}[status] || '准备就绪';
statusEl.style.color = {
loading: '#f39c12',
success: '#2ecc71',
error: '#e74c3c',
idle: '#7f8c8d',
finished: '#0022fd',
finishAll: '#ff4daf',
next: '#f39c12',
}[status];
};
// 发送请求
const sendProgress = async (videoId) => {
updateUI(currentProgress, 'loading');
let oriData = {
courseId: unsafeWindow.courseId,
itemId: unsafeWindow.p.itemId,
videoId: videoId,
playProgress: currentProgress,
segId: unsafeWindow.p.segId,
type: unsafeWindow.p.type,
tjzj: 1,
clockInDot: currentProgress,//后台要求此参数为视频播放的位置
sourceId: unsafeWindow.p.projectId,
timeLimit: unsafeWindow.timilistParam.timeLimit || -1,
originP: unsafeWindow.p.originP === 1 ? 2 : 1, // 硬编码,等待修改
}
try {
const response = await fetch(
`${requestObj.progress.url}?orgId=${unsafeWindow.p.orgId}`,
{
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-requested-with": "XMLHttpRequest",
"u-platformId": unsafeWindow.platformInfo.id
},
credentials: "include",
body: new URLSearchParams(oriData)
}
);
const data = await response.json();
console.log(data)
if (data.data?.videoProgress > 0) {
currentProgress = parseInt(data.data.videoProgress);
updateUI(currentProgress, 'success');
return data.data.progress;
} else {
throw new Error('无效的服务器响应');
}
} catch (error) {
console.error('请求失败:', error);
updateUI(currentProgress, 'error');
}
};
// 创建控制界面
function createControlPanel() {
Swal.fire({
...SWAL_CONFIG,
didOpen: (modal) => {
swalInstance = modal;
// 添加控制按钮
const actions = document.createElement('div');
actions.style = `
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 15px;
`;
const startBtn = createButton('▶ 开始', '#2ecc71', async () => {
if (!isRunning) {
try {
try {
document.querySelector('video').pause()
}catch (e) {
}
isRunning = true;
startBtn.textContent = '⏸ 暂停';
startBtn.style.background = '#e74c3c';
let courseData = getCourseData();
for (const courseDatum of courseData) {
if (!isRunning) {
return
}
await sleep(2000)
console.log(courseDatum.name)
swalInstance.querySelector('#currentVideo').textContent = courseDatum.name
currentProgress = 0;
totalTime = parseInt(courseDatum.duration);
if (parseInt(courseDatum.progress) === 1) {
console.log(" 已学完,跳过...")
updateUI(currentProgress, 'finished');
continue;
}
do {
const progress = await sendProgress(courseDatum.videoId, currentProgress); // 立即执行
if (progress === "1.0") {
// currentProgress=0;
break;
}
// currentProgress += 60
// 可中断的等待
await interruptibleWait(21000);
} while (currentProgress < totalTime && isRunning)
updateUI(currentProgress, 'next');
await sleep(20000);
}
// 非暂停结束
if(isRunning){
currentProgress = 1;
totalTime = 1;
updateUI(currentProgress, 'finishAll');
startBtn.textContent = '▶ 开始';
startBtn.style.background = '#2ecc71';
}
} catch (e) {
console.error(e)
if (Swal) {
Swal.fire({
title: "失败!",
text: e.toString() + "请在视频播放页面使用!!!",
icon: 'error',
// showCancelButton: true,
confirmButtonColor: "#FF4DAFFF",
// cancelButtonText: "取消,等会刷新",
confirmButtonText: "点击去反馈",
}).then((result) => {
if (result.isConfirmed) {
window.open("https://greasyfork.org/zh-CN/scripts/525037/feedback")
}
});
}
}finally {
isRunning = false;
}
} else {
isRunning = false;
startBtn.textContent = '▶ 继续';
startBtn.style.background = '#2ecc71';
// clearInterval(timerId);
if (checkInterval) {
clearTimeout(checkInterval.timer);
checkInterval.resolve(); // 立即结束等待
}
updateUI(currentProgress, 'idle');
setTimeout(() => {
updateUI(currentProgress, 'idle');
}, 2000)
}
});
const resetBtn = createButton('→去好评', '#dbba34', () => {
window.open("https://greasyfork.org/zh-CN/scripts/525037/feedback")
});
actions.append(startBtn, resetBtn);
modal.querySelector('.swal2-html-container').append(actions);
}
});
}
/**
* 睡眠
* @param time
* @returns {Promise}
*/
const sleep = function (time) {
return new Promise(resolve => setTimeout(resolve, time));
}
function interruptibleWait(ms) {
return new Promise(resolve => {
const timer = setTimeout(resolve, ms);
// 暴露清除方法以便立即暂停
checkInterval = {timer, resolve};
});
}
function createButton(text, color, onClick) {
const btn = document.createElement('button');
btn.textContent = text;
btn.style = `
padding: 10px 15px;
border: none;
border-radius: 5px;
background: ${color};
color: white;
cursor: pointer;
transition: opacity 0.3s;
`;
btn.addEventListener('click', onClick);
btn.addEventListener('mouseenter', () => btn.style.opacity = 0.8);
btn.addEventListener('mouseleave', () => btn.style.opacity = 1);
return btn;
}
function getCourseData() {
let courseData = unsafeWindow.initlessons
console.log(courseData)
if (!courseData) {
updateUI(currentProgress, 'error');
console.error("no course data!");
return
}
courseData = courseData.filter(item => {
return item?.type !== "1";
});
return [...courseData];
}
/********************************************************
* 打赏
*******************************************************/
GM_addStyle(`
.donate-panel {
position: fixed;
left: 30%;
top:50%;
background: linear-gradient(135deg, #fff5f5 0%, #fff0f7 100%);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(255, 77, 175, 0.2);
padding: 24px;
width: 520px;
z-index: 2147483647;
transform: translateY(-100); /* 初始隐藏位置 */
opacity: 1; /* 确保初始可见性 */
border: 1px solid #ffe6f0;
backdrop-filter: blur(8px);
transition: none; /* 禁用transition改用animation */
}
.donate-header {
position: relative;
font-size: 18px;
color: #ff4daf;
margin-bottom: 20px;
font-weight: 600;
display: flex;
align-items: center;
gap: 12px;
padding-bottom: 12px;
border-bottom: 2px solid rgba(255, 77, 175, 0.1);
}
.donate-header::after {
content: "✨";
position: absolute;
right: 0;
top: -8px;
font-size: 24px;
animation: sparkle 2s infinite;
}
.motivation-text {
font-size: 13px;
color: #666;
line-height: 1.6;
margin: 12px 0;
background: rgba(255, 255, 255, 0.9);
padding: 12px;
border-radius: 8px;
border: 1px solid #ffebf3;
}
@keyframes heartbeat {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes sparkle {
0% { opacity: 0.3; }
50% { opacity: 1; }
100% { opacity: 0.3; }
}
@keyframes panelSlideIn {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(-50%); opacity: 1; }
}
@keyframes panelSlideOut {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(100%); opacity: 0; }
}
@keyframes heartbeat {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.qr-grid {
display: grid;
grid-template-columns: 1fr; /* 改为单列布局 */
gap: 24px;
margin: 24px auto;
max-width: 300px; /* 增大容器宽度 */
}
.qr-item {
position: relative;
overflow: hidden;
border-radius: 12px;
transition: 0.3s;
padding: 12px; /* 增加内边距 */
background: #fff;
box-shadow: 0 4px 12px rgba(255, 77, 175, 0.1);
}
.qr-item:hover {
transform: translateY(-4px);
box-shadow: 0 6px 16px rgba(255, 77, 175, 0.2);
}
.qr-item img {
width: 100%;
height: auto; /* 保持比例 */
border-radius: 8px;
border: 1px solid #ffe5f0;
min-height: 280px; /* 最小高度保证 */
}
.qr-item p {
text-align: center;
margin: 16px 0 8px;
font-size: 16px; /* 增大文字 */
color: #ff4daf;
font-weight: 600;
}
/* 新增文字样式 */
.qr-tips {
text-align: center;
margin: 8px 0;
font-size: 14px;
color: #ff7ab8; /* 更柔和的粉色 */
}
.qr-proverb {
font-style: italic;
color: #ff9ec7; /* 更浅的粉色 */
font-size: 13px;
margin-top: 4px;
}
/* 修改原有.qr-item p样式 */
.qr-item p {
margin: 12px 0 4px; /* 减小下边距 */
/* 其他样式保持不变 */
}
/* 手机横屏/平板适配 */
@media (min-width: 600px) {
.qr-grid {
grid-template-columns: 1fr 1fr; /* 大屏幕恢复双列 */
max-width: 600px;
}
.qr-item img {
min-height: 240px;
}
}
.third-party {
margin-top: 20px;
}
.platform-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px;
background: linear-gradient(135deg, #fff0f5 0%, #fff8fb 100%);
border-radius: 8px;
text-decoration: none;
color: #ff6699 !important;
font-size: 14px;
margin: 8px 0;
transition: 0.3s;
border: 1px solid #ffe6ee;
}
.donate-panel.active {
animation: panelSlideIn 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.donate-panel.exit {
animation: panelSlideOut 0.3s ease forwards;
}
/* 触发按钮动画 */
#donate-trigger {
animation: heartbeat 1.8s ease-in-out infinite;
}
.platform-btn:hover {
background: linear-gradient(135deg, #ffe6ee 0%, #fff1f7 100%);
box-shadow: 0 4px 12px rgba(255, 77, 175, 0.1);
}
.close-btn {
/* 保持原有样式 */
}
`);
// 激励文案库
const motivationTexts = [
"您的每一份支持都将转化为:",
"❤️ 服务器续费 ",
"🛠️ 持续开发维护 ",
"☕ 深夜码农的咖啡燃料",
"🐈 小猫最爱的水煮鸡胸肉",
];
// 动态生成激励文案
function generateMotivation() {
const fragments = [
'',
'🌟 感谢使用本脚本!',
...motivationTexts.map(t => `• ${t}`),
'
'
].join('
');
return fragments
.replace('${donateCount}', '1,234')
.replace('${updateDays}', '365');
}
// 打赏面板HTML结构
const donateHTML = `
${generateMotivation()}
微信扫码支持
支付宝扫码支持
`;
// 初始化打赏面板
function initDonate() {
if (document.getElementById('donate-panel')) return;
const panel = document.createElement('div');
panel.innerHTML = donateHTML;
panel.className = 'donate-panel';
document.body.appendChild(panel);
// 强制重排触发动画
void panel.offsetWidth; // 触发CSS重绘
panel.classList.add('active');
// 关闭按钮事件
panel.querySelector('#donate-panel-close').addEventListener('click', () => {
panel.classList.remove('active');
panel.classList.add('exit');
panel.addEventListener('animationend', () => {
panel.remove();
}, {once: true});
});
// 点击外部关闭
const clickHandler = (e) => {
if (!panel.contains(e.target) && e.target.id !== 'donate-trigger') {
panel.classList.add('exit');
panel.addEventListener('animationend', () => {
panel.remove();
}, {once: true});
document.removeEventListener('click', clickHandler);
}
};
setTimeout(() => document.addEventListener('click', clickHandler), 100);
}
// 显示触发按钮
const trigger = document.createElement('div');
trigger.innerHTML = '❤️ 打赏支持';
Object.assign(trigger.style, {
position: 'fixed',
left: '10px',
top: '415px',
background: '#ff6b6b',
color: 'white',
padding: '8px 16px',
borderRadius: '20px',
cursor: 'pointer',
zIndex: '999999999999999',
boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
fontSize: '14px'
});
// 触发按钮增强
Object.assign(trigger.style, {
background: 'linear-gradient(135deg, #ff4daf 0%, #ff6b6b 100%)',
fontWeight: '600',
padding: '12px 24px',
boxShadow: '0 4px 24px rgba(255, 77, 175, 0.3)',
animation: 'heartbeat 1.5s ease-in-out infinite',
border: '1px solid #ffb3d9'
});
trigger.addEventListener('click', initDonate);
document.body.appendChild(trigger);
/********************************************************
* 中小学智慧教育平台 * 寒假研修
*******************************************************/
//样式
let style = `.button-3 {
position: fixed;
appearance: none;
background-color: #ed5822;
border: 1px solid rgba(27, 31, 35, .15);
border-radius: 6px;
box-shadow: rgba(27, 31, 35, .1) 0 1px 0;
box-sizing: border-box;
color: #ffffff;
cursor: pointer;
display: inline-block;
font-family: -apple-system,system-ui,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
font-size: 14px;
font-weight: 600;
line-height: 20px;
padding: 6px 16px;
left: 20px;
top: 300px;
text-align: center;
text-decoration: none;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: middle;
white-space: nowrap;
z-index: 2147483647;
}
.button-3:focus:not(:focus-visible):not(.focus-visible) {
box-shadow: none;
outline: none;
}
.button-3:hover {
background-color: #2c974b;
}
.button-3:focus {
box-shadow: rgba(46, 164, 79, .4) 0 0 0 3px;
outline: none;
}
.button-3:disabled {
background-color: #94d3a2;
border-color: rgba(27, 31, 35, .1);
color: rgba(255, 255, 255, .8);
cursor: default;
}
.button-3:active {
background-color: #298e46;
box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset;
}`
const showQQGroup = () => {
}
const createFloatingButton = () => {
// 如果按钮已存在则先移除旧实例
const existingBtn = document.getElementById('zs-helper-btn');
if (existingBtn) existingBtn.remove();
// 直接创建按钮元素(去掉外层div嵌套)
const btn = document.createElement('div');
btn.id = 'zs-helper-btn'; // 确保唯一ID直接设置在元素上
btn.style.cssText = `
position: fixed;
left: 10px;
top: 250px;
transform: translateY(-50%);
background: #ed5822;
color: white;
padding: 12px 24px;
border-radius: 30px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(255,77,175,0.3);
z-index: 2147483647; /* 使用最大z-index值 */
transition: 0.3s;
font-family: 'Microsoft Yahei', sans-serif;
white-space: nowrap;
display: flex;
align-items: center;
gap: 8px;
`;
// 添加内部HTML内容
btn.innerHTML = `
使用指南
`;
// 使用更可靠的事件监听方式
const handleHover = () => {
btn.style.transform = 'translateY(-50%) scale(1.05)';
btn.style.boxShadow = '0 6px 16px rgba(255,77,175,0.4)';
};
const handleLeave = () => {
btn.style.transform = 'translateY(-50%) scale(1)';
btn.style.boxShadow = '0 4px 12px rgba(255,77,175,0.3)';
};
btn.addEventListener('mouseenter', handleHover);
btn.addEventListener('mouseleave', handleLeave);
btn.addEventListener('click', showGuideDialog);
document.body.appendChild(btn);
return btn;
};
// 显示操作指南弹窗
const showGuideDialog = () => {
if (Swal) {
Swal.fire({
title: `📚 智能刷课指南 v${GM_info.script.version}
`,
html: `
播放页面未正常生效请刷新页面!播放页面左侧无红色按钮请刷新页面!
🚀 极速操作流程
1
进入2025研修课程播放页面 / 课程目录页面
2
直接点击相应按钮,等待操作完成后,刷新页面
3
诶个点击视频,看完最后几秒,安全保留日志信息
⚠️ 重要提醒
- 视频最后剩下5秒需要看完
- 刷课时勿播放视频
- 建议刷完全部视频再刷新,观看最后的几秒
💡 高效技巧
- 中小学,在目录或播放页。点击按钮直接开刷
- 职业/高等,挂机即可,可最小化浏览器
${renderQQGroups()}
`,
confirmButtonText: "已了解,开始减负之旅 →",
confirmButtonColor: "#FF4DAF",
showCancelButton: true,
cancelButtonText: "不在显示此窗口",
cancelButtonColor: "#95a5a6",
width: 760,
customClass: {
popup: 'animated pulse',
title: 'swal-title-custom'
},
footer: '请合理使用本工具
'
}).then((result) => {
// console.log(result);
// console.log(Swal.DismissReason.cancel);
if (result.dismiss === Swal.DismissReason.cancel) {
// 跳转到课程列表页或其他操作
localStorage.setItem('noMoreDialog', "ture")
}
});
}
}
// 初始化逻辑
// 初始化逻辑优化
const init = () => {
// 创建悬浮按钮
const floatBtn = createFloatingButton();
// 添加防DOM清理监听(优化版)
const observer = new MutationObserver(mutations => {
if (!document.body.contains(floatBtn)) {
createFloatingButton();
}
});
observer.observe(document.body, {childList: true});
// 添加CSS保护
const style = document.createElement('style');
style.textContent = `
#zs-helper-btn {
pointer-events: auto !important;
opacity: 1 !important;
visibility: visible !important;
}
#zs-helper-btn:hover {
transform: translateY(-50%) scale(1.05) !important;
}
`;
document.head.appendChild(style);
};
function getVideoTime() {
return Math.round(document.querySelector('video').duration)
}
function getResourceId() {
// 获取目标元素
const divElement = document.querySelector('div.vjs-poster');
if (divElement) {
const bgImage = divElement.style.backgroundImage;
const uuidPattern = /assets\/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/;
const match = bgImage.match(uuidPattern);
if (match) {
const resId = match[1];
console.log(resId);
return resId
}
}
throw Error("can not get ResourceId!")
}
function getResourceIdFromFullData() {
if(!fullDatas || fullDatas.nodes?.length === 0) {
throw Error("can't get fullDatas!")
}
const result = [];
// 递归遍历节点
const traverse = (node) => {
if (node.node_type === 'catalog' && node.child_nodes?.length > 0) {
// 如果是目录节点,继续遍历子节点
node.child_nodes.forEach(child => traverse(child));
} else if (node.node_type === 'activity') {
// 如果是活动节点,提取资源
const resources = node.relations?.activity?.activity_resources || [];
resources.forEach(resource => {
result.push({
name: node.node_name || '未命名课程',
resource_id: resource.resource_id || '',
studyTime:resource.study_time
});
});
}
};
// 遍历初始节点数组
fullDatas.nodes.forEach(node => traverse(node));
return result.filter(item => item.resource_id); // 过滤无效项
}
function getDynamicToken() {
try {
const pattern = /^ND_UC_AUTH-([0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12})&ncet-xedu&token$/;
for (let key of Object.keys(localStorage)) {
if (pattern.test(key)) {
return {
key: key,
appId: key.match(pattern)[1],
token: JSON.parse(JSON.parse(localStorage.getItem(key)).value)
};
}
}
throw Error("Invalid token! can not get loginInfo!");
} catch (err) {
throw Error("At:getDynamicToken>>" + err);
}
}
// const tokenData = getDynamicToken();
// if (tokenData) {
// console.log("完整键名:", tokenData.key);
// console.log("用户UUID:", tokenData.uuid);
// console.log("Token值:", tokenData.token);
// }
// 作者:zzzzzzys
// https://greasyfork.org/zh-CN/users/1176747-zzzzzzys
// 搬运可耻
const getMACAuthorizationHeaders = function (url, method) {
let n = getDynamicToken().token
return He(url, method, {
accessToken: n.access_token,
macKey: n.mac_key,
diff: n.diff
});
}
function Ze(e) {
for (var t = "0123456789ABCDEFGHIJKLMNOPQRTUVWXZYS".split(""), n = "", r = 0; r < e; r++)
n += t[Math.ceil(35 * Math.random())];
return n
}
function Fe(e) {
return (new Date).getTime() + parseInt(e, 10) + ":" + Ze(8)
}
function ze(e, t, n, r) {
let o = {
relative: new URL(e).pathname,
authority: new URL(e).hostname
}
let i = t + "\n" + n.toUpperCase() + "\n" + o.relative + "\n" + o.authority + "\n";
return CryptoJS.HmacSHA256(i, r).toString(CryptoJS.enc.Base64)
}
function He(e) {
// 作者:zzzzzzys
// https://greasyfork.org/zh-CN/users/1176747-zzzzzzys
// 搬运可耻
let t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "GET"
, n = arguments.length > 2 ? arguments[2] : void 0
, r = n.accessToken
, o = n.macKey
, i = n.diff
, s = Fe(i)
, a = ze(e, s, t, o);
return 'MAC id="'.concat(r, '",nonce="').concat(s, '",mac="').concat(a, '"')
}
const setProgress = function (url, duration) {
const info = getDynamicToken()
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
'url': url,
method: 'PUT',
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"authorization": getMACAuthorizationHeaders(url, 'PUT'),
"cache-control": "no-cache",
"pragma": "no-cache",
"content-type": "application/json",
"sdp-app-id": info.appId,
"sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"host": "x-study-record-api.ykt.eduyun.cn",
"origin": "https://basic.smartedu.cn",
"referer": "https://basic.smartedu.cn/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
},
data: JSON.stringify({position: duration - 3}),
// fetch:true,
onload: function (res) {
console.log('请求成功')
console.log(res)
if (res.status === 200) {
console.log("刷课成功!")
resolve(res)
}else {
reject('服务器拒绝:'+res.response)
}
},
onerror: function (err) {
reject('请求错误!' + err.toString())
}
})
})
}
function main () {
init()
if (!localStorage.getItem("noMoreDialog")) {
showGuideDialog()
// return
}
let myStyle = document.createElement('style')
myStyle.innerHTML = style;
document.head.appendChild(myStyle);
/*let intercept=GM_GetValue*/
let div = document.createElement('div');
div.innerHTML = `即刻开刷(中小学)
职业教育/高等教育 刷课
2222
`
document.body.appendChild(div);
const trigger = document.getElementById('my3')
trigger.addEventListener('click', () => {
if (location.href.includes("core.teacher.vocational.smartedu.cn")) {
createControlPanel()
} else {
Swal.fire({
title: "注意",
text: "请在职业/高等教育的视频播放页面使用,中小学请用上面的按钮!",
icon: 'info',
// showCancelButton: true,
confirmButtonColor: "#FF4DAFFF",
// cancelButtonText: "取消,等会刷新",
confirmButtonText: "了解~",
}).then((result) => {
});
}
});
trigger.addEventListener('mouseenter', () => trigger.style.transform = 'scale(1.05)');
trigger.addEventListener('mouseleave', () => trigger.style.transform = 'none');
let isProcessing = false;
const button = document.getElementById('my1');
button.addEventListener("click", async () => {
if (isProcessing) {
Swal.fire({
title: "操作进行中",
text: "正在刷课中,请勿重复点击!",
icon: "warning",
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "知道了"
});
return;
}
try {
isProcessing = true; // 标记开始处理
button.disabled = true; // 禁用按钮
button.textContent = "刷课进行中..."; // 修改按钮文字
let resId
const allResults = [];
try {
// resId=getResourceId()
}catch (e) {}
if(!resId){
console.log("二次获取resId...")
resId=getResourceIdFromFullData()
}
if(resId && typeof resId === 'string') {
await setProgress(requestObj.resourceLearningPositions.url + resId + '/' + getDynamicToken().token["user_id"], getVideoTime())
allResults.push({ name: '单个课程', status: 'success' });
}else if (Array.isArray(resId) && resId.length > 0) {
const results = await Promise.allSettled(
resId.map(async (item) => {
try {
await setProgress(requestObj.resourceLearningPositions.url + item.resource_id + '/' + getDynamicToken().token["user_id"], item.studyTime)
return { name:item.name, status: 'success' };
} catch (e) {
console.error(`${item.name} 失败!`, e);
return { name:item.name, status: 'fail', error: e };
}
})
);
console.log(results)
results.forEach(r => {
if (r.status === 'fulfilled') allResults.push(r.value);
else allResults.push(r.reason); // 捕获未处理的意外错误
});
}
if (Swal) {
Swal.fire({
title: "刷课成功!",
html: `
总计:${allResults.filter(r => r.status === 'success').length} 成功 / ${allResults.filter(r => r.status === 'fail').length} 失败
${allResults.map(result => `
-
${result.status === 'success' ? '✅' : '❌'}
${result.name}
${result.error ? `
${result.error.message || result.error}
` : ''}
`).join('')}
视频只剩下最后5s,需要看完,请刷新后再观看!
刷课前请勿播放视频,否则可能会导致进度更新失败!
${renderQQGroups()}
`,
icon: 'success',
confirmButtonColor: "#FF4DAFFF",
// cancelButtonText: "取消,等会刷新",
// 作者:zzzzzzys
// https://greasyfork.org/zh-CN/users/1176747-zzzzzzys
// 搬运可耻
confirmButtonText: "确定",
}).then((result) => {
if (result.isConfirmed) {
}
});
}
} catch (e) {
console.error(e)
if (Swal) {
Swal.fire({
title: "失败!",
text: e.toString() + " 请在视频播放页面使用!",
icon: 'error',
// showCancelButton: true,
confirmButtonColor: "#FF4DAFFF",
// cancelButtonText: "取消,等会刷新",
confirmButtonText: "点击去反馈",
}).then((result) => {
if (result.isConfirmed) {
window.open("https://greasyfork.org/zh-CN/scripts/525037/feedback")
}
});
}
}finally {
isProcessing = false; // 重置处理状态
button.disabled = false; // 恢复按钮
button.textContent = "即刻开刷(中小学)"; // 恢复按钮文字
}
})
document.getElementById('my2').addEventListener('click', function () {
Swal.fire({
title: '欢迎加入交流群',
html: `
${renderQQGroups()}
📚 减负工具
🛡️ 使用规范
- 仅限个人使用
- 禁止商业倒卖行为
- 禁止利用此脚本收费代刷
- 请勿批量自动化操作大量刷课(如需要请联系我,更加高效安全)
`,
icon: 'info',
confirmButtonColor: "#FF4DAF",
confirmButtonText: "2222",
showCloseButton: true,
width: 680,
showDenyButton: true,
denyButtonText: '
前往好评', // 带图标的按钮
denyButtonColor: '#FFC107',
focusDeny: false,
showCancelButton: false,
// 新增按钮回调
preDeny: () => {
window.open("https://greasyfork.org/zh-CN/scripts/525037/feedback", "_blank");
return false; // 阻止弹窗关闭
},
customClass: {
denyButton: 'swal-custom-deny',
popup: 'swal-custom-popup',
title: 'swal-custom-title'
},
footer: '请合理使用。
'
});
});
}
main()
})();