职业成长自动轮播学习脚本分享
# 平台情况
职业成长这个平台挺有意思的,网址是 https://www.zyczedu.com/dashboard 。上次有个上海的学员找我,说他们单位要求在这上面学职业技能课,每天还得刷够学时才能达标。那天下午我刚喝完咖啡,就顺手研究了一下这个平台。
说实话,职业成长的视频播放器设计得还算中规中矩,进度条能看能拖拽。问题就是它那个"我已学完"按钮,得视频播完了才能点,有时候网络卡一下或者出去接个电话,回来发现视频停了还得手动重新开始。而且一节课学完得自己去找下一节,课程列表里几十个视频,眼睛都看花了。
# 脚本功能
针对职业成长平台的特点,脚本实现了以下功能:
自动播放视频,会自动尝试播放当前视频,如果失败会尝试静音播放。自动提交进度,检测到视频快学完时自动点击"我已学完"按钮。自动切换课程,学完当前课后自动跳转到下一个未完成的视频。还有个可视化面板,能显示当前状态、剩余未学完课程数、学习进度等,而且可以随时暂停或开始自动学习。
脚本安装地址:
https://scriptcat.org/zh-CN/script-show-page/5601 (opens new window)
# 代学服务
提示
如需代学,请联系客服,支持闲鱼交易。

微信联系:yizhituziang

QQ联系:2422270452
- img: /img/weixin.jpg
name: 微信联系:yizhituziang
- img: /img/qq.jpg
name: QQ联系:2422270452
如果不会安装脚本,请按照下面安装教程来操作。
# 安装教程
# 1.安装浏览器扩展插件
首先需要给浏览器安装上脚本猫插件,这是运行所有用户脚本的基础。如果浏览器已经安装过脚本猫或者油猴插件,可以跳过这一步。推荐使用Edge浏览器,安装插件更方便。
浏览器打开网址:https://docs.scriptcat.org/ (opens new window)
用Edge浏览器作为示范,点击 "添加到Edge浏览器"

接着点击 "获取"

在右上角弹出的窗口,点击 "添加扩展"

等待几秒钟,会提示已经安装好脚本猫插件了。

# 2.安装刷课脚本
打开脚本安装地址后,在页面点击 "安装脚本" 按钮,接着在弹出的窗口点击 "安装" ,之后会提示"安装成功"。
# 3.体验脚本功能
安装脚本后,需要重新进入学习站点,如果之前已经打开课程学习页面,需要刷新页面后脚本才会生效。
# 使用注意
浏览器推荐用Chrome或者Edge,对视频支持比较稳。脚本带有可视化面板,页面右上角会显示"自动学习面板",可以看到当前状态、剩余课程数、学习进度等信息。想暂停的时候直接点面板上的"暂停自动学习"按钮就行。
进度同步方面,平台会定期保存学习进度,脚本主要负责视频自动播放和切换部分。
# 技术原理
脚本主要逻辑是每2秒检查一次视频状态。如果视频暂停了就自动播放,如果视频播放完了就自动点击"我已学完"按钮,然后切换到下一个未完成的课程。
脚本还有个智能判断逻辑,通过对比已学习时间和视频总时长来判断是否接近完成,这样即使网络卡顿导致进度有点偏差,也能正常提交。提交按钮有8秒冷却时间,避免频繁点击出问题。
# 常见问题
脚本安装地址在上面,有需要找客服。视频加载不出来可以刷新重试,脚本会自动重新开始检测。浏览器推荐Chrome或Edge,其他浏览器可能兼容性问题。多标签页同时刷的话,平台可能会检测到同一账号多处登录。
# 核心代码
(function () {
'use strict';
const LOOP_MS = 2000;
const NEXT_DELAY_MS = 4000;
const SUBMIT_COOLDOWN_MS = 8000;
const STORAGE_KEY_ENABLED = 'zyczedu-auto-study-enabled';
const isMainPage = location.pathname.indexOf('/player/study/index') !== -1;
const isPlayerFrame = location.pathname.indexOf('/js/player/pages/play.html') !== -1;
function isAutomationEnabled() {
try {
return localStorage.getItem(STORAGE_KEY_ENABLED) !== '0';
} catch (error) {
return true;
}
}
function tryPlayVideo(video) {
if (!video) return;
if (!video.paused && !video.ended) return;
const playPromise = video.play();
if (playPromise && typeof playPromise.catch === 'function') {
playPromise.catch(function () {
try {
video.muted = true;
video.play().catch(function () {});
} catch (error) {
void error;
}
});
}
}
function getVideosFromDocument(doc) {
if (!doc) return [];
return Array.from(doc.querySelectorAll('video'));
}
function click(element) {
if (!element) return false;
element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
return true;
}
function parseTimeToSeconds(value) {
if (!value) return 0;
const parts = value.trim().split(':').map(Number);
if (parts.some(Number.isNaN)) return 0;
if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2];
if (parts.length === 2) return parts[0] * 60 + parts[1];
return parts[0] || 0;
}
function isCurrentDone() {
const submitButton = document.querySelector('#btn_submit');
const hiddenStatus = document.querySelector('#hiddenStudyStatus');
if (hiddenStatus && String(hiddenStatus.value || '').trim() === '2') return true;
if (submitButton && submitButton.disabled && submitButton.classList.contains('btn-dark')) return true;
const active = document.querySelector('#list_chapter .section.active');
if (active && active.querySelector('.status-done')) return true;
return false;
}
function submitCurrent() {
const button = document.querySelector('#btn_submit');
if (!button || button.disabled) return false;
click(button);
return true;
}
function mainPageHelper() {
let lastSubmitAt = 0;
let lastNextAt = 0;
let endedCourseId = '';
function getFrameDocument() {
const iframe = document.querySelector('#container');
if (!iframe) return null;
try {
return iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document) || null;
} catch (error) {
return null;
}
}
function getPendingSections() {
return Array.from(document.querySelectorAll('#list_chapter .section')).filter(function (section) {
return !section.querySelector('.status-done');
});
}
function getCurrentVideo() {
const frameDoc = getFrameDocument();
const videos = getVideosFromDocument(frameDoc).concat(getVideosFromDocument(document));
return videos[0] || null;
}
function getCurrentCourseId() {
const active = document.querySelector('#list_chapter .section.active');
const hiddenCourseId = document.querySelector('#hiddenCourseId');
return (hiddenCourseId && hiddenCourseId.value) || (active && active.getAttribute('data-jhx-res')) || '';
}
function shouldSubmitCurrent() {
if (isCurrentDone()) return false;
const durationStr = document.querySelector('#durationStr');
const learnedStr = document.querySelector('#learnedStr');
const duration = parseTimeToSeconds(durationStr ? durationStr.textContent : '');
const learned = parseTimeToSeconds(learnedStr ? learnedStr.textContent : '');
const video = getCurrentVideo();
const nearDone = duration > 0 && learned >= Math.max(duration - 1, 0);
if (duration > 0 && learned >= duration) return true;
if (video && video.ended && nearDone) return true;
const courseId = getCurrentCourseId();
if (courseId && endedCourseId === courseId && nearDone) return true;
return false;
}
function goToFirstPending() {
const sections = getPendingSections();
if (!sections.length) return false;
const active = document.querySelector('#list_chapter .section.active');
if (active === sections[0]) return true;
click(sections[0]);
lastNextAt = Date.now();
endedCourseId = '';
return true;
}
function handleEndedBinding() {
const video = getCurrentVideo();
const courseId = getCurrentCourseId();
if (!video || !courseId) return;
if (video.dataset.autoStudyBound === courseId) return;
video.dataset.autoStudyBound = courseId;
video.addEventListener('ended', function () {
endedCourseId = courseId;
});
}
function tick() {
if (!isAutomationEnabled()) return;
const frameDoc = getFrameDocument();
const videos = getVideosFromDocument(frameDoc).concat(getVideosFromDocument(document));
videos.forEach(tryPlayVideo);
handleEndedBinding();
if (isCurrentDone()) {
if (Date.now() - lastNextAt >= 4000) goToFirstPending();
return;
}
if (shouldSubmitCurrent() && Date.now() - lastSubmitAt >= 8000) {
if (submitCurrent()) lastSubmitAt = Date.now();
}
}
tick();
window.setInterval(tick, 2000);
}
if (isPlayerFrame) {
function frameVideoHelper() {
function tick() {
const videos = getVideosFromDocument(document);
if (isAutomationEnabled()) {
videos.forEach(tryPlayVideo);
}
}
tick();
window.setInterval(tick, 2000);
}
frameVideoHelper();
return;
}
if (isMainPage) {
mainPageHelper();
}
})();