广东二师在线刷课脚本分享
# 写在前面
前阵子肇庆的陈老师找到我,说他们在广东二师在线上学习,网址是 https://istudy.gdei.edu.cn/#/login 。这是广东第二师范学院做的在线培训平台,专门给省内教师提供继续教育服务的。广东省教师继续教育要求每五年修满360个学时,平均每年大几十个。陈老师白天上课备课改作业,晚上回家累得不行,根本没时间看那些一两个小时的视频。
后来我花了两天研究这个平台,发现用脚本能实现很大程度的自动化。让视频自动播放、遇到问题自动恢复、播完自动跳下一节,测试题还是得自己做但至少不用一直盯着屏幕。脚本安装地址目前显示暂时下架,有需要的看看页面底部联系方式。
# 平台情况
广东二师在线网址是 https://istudy.gdei.edu.cn/#/login ,登录后界面左边有课程分类,右边是课程列表。课程分公需课、专业课、选修课等,每门课若干小节,每小节是视频加练习题。
平台用的是通用视频方案,兼容性还行。但有个烦人的问题——防挂机检测。超过一定时间没操作就会暂停视频。广州的赵老师吐槽说,她好不容易找到时间看课,刚看十几分钟被打断,回来发现视频早停了。进度同步方面还算稳定,但有时视频加载慢,特别是在网络高峰期。
提示
如需代学,请联系客服,支持闲鱼交易。

微信联系:yizhituziang

QQ联系:2422270452
- img: /img/weixin.jpg
name: 微信联系:yizhituziang
- img: /img/qq.jpg
name: QQ联系:2422270452
# 脚本功能
针对这个平台开发的脚本实现了以下功能:
视频自动播放,持续监测状态,暂停了自动重新开始。倍速调节默认1.5倍,完全不影响理解。防挂机检测模拟,每隔一段时间自动模拟用户操作,用随机间隔更自然。课程自动切换,播完自动点下一节。静音模式和卡顿自动处理。
脚本安装地址暂时下架了,有需要看页面底部联系方式。
# 使用场景
第一种是平时忙的,像陈老师那样白天没时间,晚上累得不行,用脚本挂着让视频自己跑。第二种是拖延症晚期,去年佛山某朋友国庆前两天才想起还有十几节课没刷,连续熬通宵。用脚本至少能省一半时间。第三种是内容重复的,每年课程差不多,用脚本挂着跳过。第四种是想边刷边做别的,挂着1.5倍大概了解内容框架。
# 使用建议
倍速建议先从1.25倍开始,感觉影响不大再调到1.5倍。不要一上来就用2倍速,可能触发风控。浏览器推荐Chrome或Edge,360要用极速模式。进度每隔一段时间检查一下,确保同步到服务器。测试题看完视频后认真做,都是知识点。账号安全方面不要用来源不明的脚本。
# 技术细节
平台的整体架构是前后端分离设计,前端Vue框架,视频播放器嵌套在好几层iframe里面。防挂机检测综合了多个维度判断用户是否在看课,不只是检测键盘鼠标事件。脚本采用了随机延迟和随机轨迹的方式让模拟更自然。进度同步用AJAX轮询方式,间隔比较长,脚本会尽量多触发几次同步操作减少进度丢失。
# 常见问题
多标签页同时刷不同课程是不行的,平台会检测到同一账号多处登录。倍速设置目前稳定,但不排除以后平台升级会改。部分视频有防挂机限制,离开太久会自动暂停。建议一个课程刷完再开另一个,不要同时开好几个窗口。Chrome和Edge最稳,Firefox和Safari理论上也行,IE就别试了。
# 结束语
广东二师在线是个还算不错的教师培训平台,课程内容挺实在的。脚本能帮你自动完成视频观看部分,倍速建议1.5倍,浏览器推荐Chrome或Edge,安装地址暂时下架需要找其他方式。有问题可以在下面留言,祝学习顺利。
# 核心代码
(function() {
'use strict';
const CONFIG = {
speed: 1.5,
checkInterval: 600,
activityInterval: 8000,
maxRetryAttempts: 4,
initialDelay: 2500
};
let retryCount = 0;
let previousTime = 0;
function locateVideoElement() {
const selectors = [
'video',
'#video_player video',
'.video-player video',
'[class*="player"] video',
'[id*="video"] video'
];
for (const selector of selectors) {
const candidate = document.querySelector(selector);
if (candidate) return candidate;
}
const iframes = document.querySelectorAll('iframe');
for (const iframe of iframes) {
try {
const innerVideo = iframe.contentDocument.querySelector('video');
if (innerVideo) return innerVideo;
} catch (e) {}
}
return null;
}
function waitForVideo(callback) {
let attempts = 0;
const interval = setInterval(() => {
const video = locateVideoElement();
if (video || attempts > 20) {
clearInterval(interval);
callback(video);
}
attempts++;
}, CONFIG.initialDelay);
}
function setPlaybackSpeed(video) {
if (video && video.playbackRate !== CONFIG.speed) {
video.playbackRate = CONFIG.speed;
}
}
function startPlayback(video) {
if (video && video.paused) {
video.play().catch(() => {
retryCount++;
if (retryCount < CONFIG.maxRetryAttempts) {
setTimeout(() => startPlayback(video), 1500);
}
});
}
}
function checkStall(video) {
if (video && !video.paused && video.currentTime === previousTime && video.readyState > 0) {
video.pause();
setTimeout(() => video.play(), 800);
}
previousTime = video ? video.currentTime : 0;
}
function simulateActivity() {
const events = ['mousemove', 'click', 'keydown'];
events.forEach(type => {
document.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true }));
});
}
function findNextButton() {
const selectors = ['.next-btn', '.next-lesson', '[class*="next"]', 'button.next'];
for (const sel of selectors) {
const btn = document.querySelector(sel);
if (btn && btn.offsetParent !== null) return btn;
}
return null;
}
function initialize() {
waitForVideo(video => {
if (!video) return;
setPlaybackSpeed(video);
startPlayback(video);
setInterval(() => setPlaybackSpeed(video), CONFIG.checkInterval);
setInterval(() => checkStall(video), CONFIG.checkInterval);
setInterval(simulateActivity, CONFIG.activityInterval);
video.addEventListener('ended', () => {
const nextBtn = findNextButton();
if (nextBtn) setTimeout(() => nextBtn.click(), 1500);
});
});
}
document.readyState === 'loading'
? document.addEventListener('DOMContentLoaded', initialize)
: initialize();
})();