// ==UserScript==
// @name 人工智能2026观看脚本V1.6 -仅供学习
// @namespace http://tampermonkey.net/
// @version 1.6
// @description 优化Bug
// @author 诺亚
// @match *://*.smartedu.cn/*
// @match https://higher.smartedu.cn/*
// @match https://service.icourses.cn/*
// @grant GM_addStyle
// @grant GM_openInTab
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
try {
const blockEvents = ['visibilitychange', 'webkitvisibilitychange', 'blur', 'pagehide', 'focusout', 'freeze', 'resume'];
blockEvents.forEach(eventName => {
window.addEventListener(eventName, e => e.stopImmediatePropagation(), true);
document.addEventListener(eventName, e => e.stopImmediatePropagation(), true);
});
Object.defineProperty(document, 'visibilityState', {
get: () => 'visible',
configurable: true
});
Object.defineProperty(document, 'hidden', {
get: () => false,
configurable: true
});
Object.defineProperty(window, 'hasFocus', {
value: () => true,
configurable: true
});
// 拦截 document.hasFocus() 调用
if (document.hasFocus) {
Object.defineProperty(document, 'hasFocus', {
value: () => true,
configurable: true
});
}
// 拦截页面可见性API的媒体查询
try {
const originalMatchMedia = window.matchMedia;
window.matchMedia = function(query) {
if (query && query.includes('prefers-reduced-motion')) {
return originalMatchMedia.call(this, query);
}
if (query && (query.includes('display-mode') || query.includes('prefers-color-scheme'))) {
return originalMatchMedia.call(this, query);
}
const result = originalMatchMedia.call(this, query);
if (query && query.includes('prefers-reduced-transparency')) {
return result;
}
// 对可见性相关查询返回伪造结果
return { matches: false, media: query, addListener: function(){}, removeListener: function(){}, addEventListener: function(){}, removeEventListener: function(){}, dispatchEvent: function(){ return true; }, onchange: null };
};
} catch(e) {}
console.log("🚀 终极挂课脚本 V1.5 已启动!切屏/最小化/后台冻结检测已完全拦截");
} catch (e) {
console.warn("⚠️ 部分防切屏功能受限,但核心播放不受影响");
}
GM_addStyle(`
#auto-study-panel {
position: fixed;
top: 20px;
right: 20px;
width: 340px;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
z-index: 99999999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: #ffffff;
overflow: hidden;
transition: all 0.3s ease;
user-select: none;
border: 1px solid rgba(255, 255, 255, 0.1);
}
#auto-study-panel.minimized {
width: 60px;
height: 60px;
border-radius: 50%;
}
#auto-study-panel.minimized .panel-content {
display: none;
}
#auto-study-panel.minimized .panel-header {
height: 60px;
padding: 0;
justify-content: center;
}
#auto-study-panel.minimized .panel-title {
display: none;
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
background: rgba(255, 255, 255, 0.05);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
cursor: move;
}
.panel-title {
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.panel-title::before {
content: '';
width: 8px;
height: 8px;
background: #10b981;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.panel-controls {
display: flex;
gap: 8px;
}
.panel-btn {
width: 28px;
height: 28px;
border: none;
border-radius: 8px;
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
font-size: 14px;
}
.panel-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.panel-btn.minimize-btn {
font-size: 18px;
}
.panel-content {
padding: 20px;
}
.status-section {
margin-bottom: 20px;
}
.status-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.status-label {
color: #94a3b8;
}
.status-value {
font-weight: 500;
color: #ffffff;
}
.status-value.running {
color: #10b981;
}
.status-value.paused {
color: #f59e0b;
}
.status-value.stopped {
color: #ef4444;
}
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
overflow: hidden;
margin-top: 8px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #10b981, #06b6d4);
border-radius: 4px;
transition: width 0.3s ease;
}
.settings-section {
margin-bottom: 20px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.settings-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #94a3b8;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.setting-label {
font-size: 13px;
color: #cbd5e1;
}
.setting-input {
width: 70px;
padding: 6px 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
background: rgba(255, 255, 255, 0.05);
color: #ffffff;
font-size: 13px;
text-align: center;
}
.setting-input:focus {
outline: none;
border-color: #10b981;
}
.action-buttons {
display: flex;
gap: 10px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.action-btn {
flex: 1;
padding: 12px 0;
border: none;
border-radius: 10px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.action-btn.expand-btn {
background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%);
color: #ffffff;
}
.action-btn.expand-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
}
.action-btn.start-btn {
background: linear-gradient(135deg, #10b981 0%, #06b6d4 100%);
color: #0a0a0a;
}
.action-btn.start-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
}
.action-btn.pause-btn {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: #0a0a0a;
}
.action-btn.pause-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);
}
.action-btn.stop-btn {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: #ffffff;
}
.action-btn.stop-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
}
.action-btn.feedback-btn {
background: linear-gradient(135deg, #ec4899 0%, #db2777 100%);
color: #ffffff;
flex: 1 1 100%;
}
.action-btn.feedback-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(236, 72, 153, 0.4);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
.recommend-section {
margin-bottom: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.recommend-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #94a3b8;
}
.recommend-buttons {
display: flex;
flex-direction: column;
gap: 8px;
}
.recommend-btn {
width: 100%;
padding: 10px 0;
border: none;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
}
.recommend-btn.general {
background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%);
color: #ffffff;
}
.recommend-btn.ai-x {
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: #ffffff;
}
.recommend-btn.llm {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: #0a0a0a;
}
.recommend-btn:hover {
transform: translateY(-1px);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
}
.log-section {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.log-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 10px;
color: #94a3b8;
display: flex;
justify-content: space-between;
align-items: center;
}
.clear-log-btn {
font-size: 12px;
color: #94a3b8;
background: none;
border: none;
cursor: pointer;
}
.clear-log-btn:hover {
color: #ffffff;
}
.log-container {
height: 140px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 10px;
font-size: 12px;
font-family: 'Consolas', monospace;
color: #e2e8f0;
}
.log-container::-webkit-scrollbar {
width: 4px;
}
.log-container::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
}
.log-item {
margin-bottom: 4px;
line-height: 1.4;
}
.log-item.info {
color: #06b6d4;
}
.log-item.success {
color: #10b981;
}
.log-item.warning {
color: #f59e0b;
}
.log-item.error {
color: #ef4444;
}
.platform-badge {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
margin-left: 6px;
background: #10b981;
color: #0a0a0a;
}
.detect-section {
margin-bottom: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.detect-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #94a3b8;
display: flex;
justify-content: space-between;
align-items: center;
}
.detect-summary {
display: flex;
gap: 10px;
margin-bottom: 12px;
}
.detect-stat {
flex: 1;
padding: 8px;
border-radius: 8px;
text-align: center;
font-size: 12px;
}
.detect-stat.completed {
background: rgba(16, 185, 129, 0.15);
color: #10b981;
border: 1px solid rgba(16, 185, 129, 0.3);
}
.detect-stat.incomplete {
background: rgba(239, 68, 68, 0.15);
color: #ef4444;
border: 1px solid rgba(239, 68, 68, 0.3);
}
.detect-stat-num {
font-size: 20px;
font-weight: 700;
display: block;
margin-bottom: 2px;
}
.detect-stat-label {
font-size: 11px;
opacity: 0.8;
}
.detect-list {
max-height: 200px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 8px;
margin-bottom: 12px;
}
.detect-list::-webkit-scrollbar {
width: 4px;
}
.detect-list::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
}
.detect-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
border-radius: 6px;
margin-bottom: 4px;
font-size: 12px;
transition: background 0.2s;
}
.detect-item:hover {
background: rgba(255, 255, 255, 0.05);
}
.detect-item-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #e2e8f0;
margin-right: 8px;
}
.detect-item-progress {
font-size: 11px;
font-weight: 600;
white-space: nowrap;
padding: 2px 8px;
border-radius: 4px;
}
.detect-item-progress.done {
background: rgba(16, 185, 129, 0.2);
color: #10b981;
}
.detect-item-progress.partial {
background: rgba(245, 158, 11, 0.2);
color: #f59e0b;
}
.detect-item-progress.none {
background: rgba(239, 68, 68, 0.2);
color: #ef4444;
}
.detect-btn-row {
display: flex;
gap: 8px;
}
.action-btn.detect-btn {
background: linear-gradient(135deg, #06b6d4 0%, #0284c7 100%);
color: #ffffff;
}
.action-btn.detect-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.4);
}
.action-btn.continue-incomplete-btn {
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
color: #ffffff;
}
.action-btn.continue-incomplete-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.4);
}
.detect-empty {
text-align: center;
color: #64748b;
font-size: 12px;
padding: 16px 0;
}
`);
let isRunning = false;
let isPaused = false;
let currentIndex = 0;
let totalVideos = 0;
let videoItems = [];
let pauseResolve = null;
let stopRequested = false;
let isAllExpanded = false;
let isSmartEduPlatform = false;
const config = {
scrollDelay: 1500,
loadDelay: 5000,
closeDelay: 3000,
checkInterval: 2000,
maxTimeout: 900
};
const recommendCourses = [
{
name: "人工智能应用导论(通识课)",
url: "https://higher.smartedu.cn/course/agc3/69ba53600976b58e126c4d8e?type=training",
class: "general"
},
{
name: "自动驾驶场景设计(AI+X)",
url: "https://higher.smartedu.cn/course/agc3/69b56a1ff74ce762ce352749?type=training",
class: "ai-x"
},
{
name: "DeepSeek大模型前沿进展",
url: "https://higher.smartedu.cn/course/lmc/67d7cf98625fca5f6cf9076e",
class: "llm"
}
];
function createPanel() {
if (document.getElementById('auto-study-panel')) {
console.log("⚠️ 面板已存在,跳过创建");
return;
}
const panel = document.createElement('div');
panel.id = 'auto-study-panel';
panel.innerHTML = `
🔥 推荐课程快速跳转
${recommendCourses.map(course =>
``
).join('')}
`;
document.body.appendChild(panel);
bindEvents();
makeDraggable(panel);
log('info', '✅ 终极挂课脚本 V5.4.1 已加载完成');
log('success', '🛡️ 切屏/最小化检测已完全拦截,后台挂机零中断');
if (isSmartEduPlatform) {
log('success', '🎯 检测到高等教育智慧教育平台,已强制启用figure标签专属脚本');
log('info', 'ℹ️ 完全按照你提供的脚本逻辑执行,无需展开章节');
log('info', '💡 已完成(100%)的视频自动跳过,中间进度从上次位置续播');
} else {
log('info', 'ℹ️ 通用模式已启用,请先点击"展开全部"再开始学习');
log('info', '💡 已完成(100%)的视频自动跳过,中间进度从上次位置续播');
}
console.log("✅ UI面板创建成功,开始按钮已绑定事件");
}
function bindEvents() {
const startBtn = document.getElementById('start-btn');
const newStartBtn = startBtn.cloneNode(true);
startBtn.parentNode.replaceChild(newStartBtn, startBtn);
newStartBtn.addEventListener('click', () => {
console.log("🔘 开始学习按钮被点击!");
log('info', '🔘 开始学习按钮被点击');
startAutoStudy();
});
document.getElementById('expand-btn').addEventListener('click', expandAllChapters);
document.getElementById('pause-btn').addEventListener('click', togglePause);
document.getElementById('stop-btn').addEventListener('click', stopAutoStudy);
document.getElementById('minimize-btn').addEventListener('click', toggleMinimize);
document.getElementById('clear-log-btn').addEventListener('click', clearLog);
document.getElementById('feedback-btn').addEventListener('click', () => {
GM_openInTab('tencent://message/?uin=723167066&Site=&Menu=yes', { active: true });
log('info', '🔗 正在打开QQ反馈窗口');
});
document.querySelectorAll('.recommend-btn').forEach(btn => {
btn.addEventListener('click', () => {
const url = btn.getAttribute('data-url');
GM_openInTab(url, { active: true });
log('info', `🔗 正在打开推荐课程: ${btn.textContent}`);
});
});
document.getElementById('scroll-delay').addEventListener('change', (e) => {
if (!isSmartEduPlatform) config.scrollDelay = parseInt(e.target.value) || 1500;
});
document.getElementById('load-delay').addEventListener('change', (e) => {
if (!isSmartEduPlatform) config.loadDelay = parseInt(e.target.value) || 5000;
});
document.getElementById('close-delay').addEventListener('change', (e) => {
if (!isSmartEduPlatform) config.closeDelay = parseInt(e.target.value) || 3000;
});
document.getElementById('detect-btn').addEventListener('click', detectCourseProgress);
document.getElementById('continue-incomplete-btn').addEventListener('click', continueIncomplete);
document.getElementById('detect-close-btn').addEventListener('click', () => {
document.getElementById('detect-section').style.display = 'none';
});
console.log("✅ 所有事件绑定完成");
}
function makeDraggable(element) {
const header = document.getElementById('panel-header');
let isDragging = false;
let offsetX, offsetY;
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.panel-controls')) return;
isDragging = true;
offsetX = e.clientX - element.offsetLeft;
offsetY = e.clientY - element.offsetTop;
element.style.zIndex = '99999999';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
element.style.left = `${x}px`;
element.style.top = `${y}px`;
element.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
}
async function expandAllChapters() {
if (isSmartEduPlatform) {
log('warning', '⚠️ 智慧教育平台专属模式无需展开章节');
return;
}
log('info', '🔍 正在扫描所有章节...');
try {
const mainChapters = document.querySelectorAll('li.list-item');
log('info', `📚 找到 ${mainChapters.length} 个大章节`);
for (let i = 0; i < mainChapters.length; i++) {
if (stopRequested) break;
const chapter = mainChapters[i];
const leftItem = chapter.querySelector('.left-item');
const icon = leftItem?.querySelector('.icon');
if (leftItem && icon && !icon.classList.contains('is-expanded')) {
log('info', `📖 展开大章节 ${i + 1}/${mainChapters.length}`);
safeClick(leftItem);
await randomSleep(400, 800);
}
}
const subChapters = document.querySelectorAll('.ant-collapse-item');
log('info', `📑 找到 ${subChapters.length} 个小节`);
for (let i = 0; i < subChapters.length; i++) {
if (stopRequested) break;
const chapter = subChapters[i];
const header = chapter.querySelector('.ant-collapse-header');
if (header && header.getAttribute('aria-expanded') !== 'true') {
log('info', `📖 展开小节 ${i + 1}/${subChapters.length}`);
safeClick(header);
await randomSleep(400, 800);
}
}
await sleep(1500);
videoItems = Array.from(document.querySelectorAll('.name, .child-item')).filter(item => {
return item.offsetParent !== null;
});
totalVideos = videoItems.length;
if (totalVideos === 0) {
log('warning', '⚠️ 未找到任何可见的视频项');
} else {
log('success', `🎉 所有章节已成功展开,共找到 ${totalVideos} 个可见视频`);
}
isAllExpanded = true;
updateUI();
} catch (error) {
log('error', `❌ 展开章节时出错: ${error.message}`);
console.error(error);
}
}
// 使用Web Worker保持后台定时器精度(防止浏览器降频setInterval)
let keepAliveWorker = null;
function startKeepAliveWorker() {
try {
const workerCode = `
let intervalId = null;
self.onmessage = function(e) {
if (e.data === 'start') {
intervalId = setInterval(() => {
self.postMessage('tick');
}, 1000);
} else if (e.data === 'stop') {
if (intervalId) clearInterval(intervalId);
self.close();
}
};
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
keepAliveWorker = new Worker(URL.createObjectURL(blob));
keepAliveWorker.onmessage = function(e) {
if (e.data === 'tick') {
// Worker每秒发送tick,保持主线程定时器活跃
}
};
keepAliveWorker.postMessage('start');
console.log("🛡️ 后台保活Worker已启动,防止定时器降频");
} catch(e) {
console.warn("⚠️ Web Worker启动失败,后台定时器可能被降频:", e);
}
}
function stopKeepAliveWorker() {
if (keepAliveWorker) {
keepAliveWorker.postMessage('stop');
keepAliveWorker = null;
}
}
async function startAutoStudy() {
console.log("🚀 startAutoStudy函数被调用");
if (isRunning) {
log('warning', '⚠️ 脚本已经在运行中');
console.log("⚠️ 脚本已经在运行中,isRunning =", isRunning);
return;
}
startKeepAliveWorker();
try {
if (isSmartEduPlatform) {
await startSmartEduExclusiveAutoStudy();
} else {
await startIntegratedAutoStudy();
}
} catch (error) {
log('error', `❌ 学习过程中出错: ${error.message}`);
console.error("❌ 学习过程中出错:", error);
}
isRunning = false;
isPaused = false;
stopKeepAliveWorker();
updateUI();
console.log("🏁 学习流程结束,isRunning已重置为false");
}
// 存储检测结果
let detectedItems = [];
async function detectCourseProgress() {
if (isRunning) {
log('warning', '⚠️ 脚本运行中,请先停止再检测');
return;
}
log('info', '🔍 正在扫描课程进度...');
detectedItems = [];
if (isSmartEduPlatform) {
// 智慧教育平台:兼容多种元素结构扫描视频
let foundItems = null;
for (let retry = 0; retry < 3; retry++) {
foundItems = findAllVideoItems();
if (foundItems.length > 0) break;
log('warning', `⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`);
await sleep(2000);
}
if (!foundItems || foundItems.length === 0) {
log('error', '❌ 未找到视频,请确认页面已完全加载');
return;
}
foundItems.forEach((item, idx) => {
const name = getItemName(item, idx);
const progress = getItemProgressText(item);
const isComplete = checkSmartEduItemCompleted(item);
detectedItems.push({ name, progress, isComplete, element: item, index: idx });
});
} else {
// 通用模式:需要先展开
if (!isAllExpanded) {
log('info', '🔄 先展开章节...');
await expandAllChapters();
}
videoItems.forEach((item, idx) => {
const processElement = item.querySelector('.process');
const processText = processElement ? (processElement.innerText || '').trim() : '';
const parentText = item.parentElement ? item.parentElement.innerText : item.innerText;
const name = getItemName(item, idx);
const progress = processText || getItemProgressText(item);
// 只有"已观看100%"或"已完成"才算完成
const isComplete = processText.includes('已观看100%') || processText.includes('已完成') ||
(processText === '' && (parentText.includes('已观看100%') || parentText.includes('已完成')));
detectedItems.push({ name, progress, isComplete, element: item, index: idx });
});
}
const completedCount = detectedItems.filter(i => i.isComplete).length;
const incompleteCount = detectedItems.length - completedCount;
// 显示检测区域
const detectSection = document.getElementById('detect-section');
detectSection.style.display = 'block';
document.getElementById('detect-completed-num').textContent = completedCount;
document.getElementById('detect-incomplete-num').textContent = incompleteCount;
// 渲染列表
const listEl = document.getElementById('detect-list');
if (detectedItems.length === 0) {
listEl.innerHTML = '未找到视频
';
} else {
listEl.innerHTML = detectedItems.map((item, idx) => {
const statusClass = item.isComplete ? 'done' : (item.progress ? 'partial' : 'none');
const statusText = item.isComplete ? '✅ 已完成' : (item.progress || '❌ 未观看');
return `
${idx + 1}. ${item.name}
${statusText}
`;
}).join('');
}
// 启用/禁用"续播未完成"按钮
document.getElementById('continue-incomplete-btn').disabled = incompleteCount === 0;
if (incompleteCount === 0) {
log('success', `🎉 检测完成:共 ${detectedItems.length} 个视频,全部已完成!`);
} else {
log('success', `📊 检测完成:共 ${detectedItems.length} 个视频,已完成 ${completedCount} 个,未完成 ${incompleteCount} 个`);
log('info', `💡 点击"续播未完成"按钮只播放未完成的视频`);
}
}
async function continueIncomplete() {
if (isRunning) {
log('warning', '⚠️ 脚本运行中,请先停止');
return;
}
const incompleteItems = detectedItems.filter(i => !i.isComplete);
if (incompleteItems.length === 0) {
log('info', '✅ 没有未完成的视频');
return;
}
log('info', `▶️ 开始续播 ${incompleteItems.length} 个未完成视频`);
isRunning = true;
isPaused = false;
stopRequested = false;
startKeepAliveWorker();
updateUI();
try {
if (isSmartEduPlatform) {
await continueSmartEduIncomplete(incompleteItems);
} else {
await continueGenericIncomplete(incompleteItems);
}
} catch (error) {
log('error', `❌ 续播出错: ${error.message}`);
console.error(error);
}
isRunning = false;
isPaused = false;
stopKeepAliveWorker();
updateUI();
}
async function continueSmartEduIncomplete(items) {
totalVideos = items.length;
currentIndex = 0;
for (let i = 0; i < items.length; i++) {
if (stopRequested) break;
currentIndex = i + 1;
updateUI();
const { element, name } = items[i];
if (isPaused) {
log('info', '⏸️ 已暂停,等待继续...');
await new Promise(resolve => { pauseResolve = resolve; });
if (stopRequested) break;
log('info', '▶️ 继续学习');
}
log('info', `▶️ [续播] ${i + 1}/${items.length} - ${name}`);
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(1500);
safeClick(element);
await sleep(4000);
await forcePlayVideo();
await watchSmartEduVideoUntilEnd(i + 1);
// 关闭弹窗
let closeBtn = document.querySelector('button[class*="bg-white"][class*="h-10"]');
if (!closeBtn) closeBtn = document.querySelector('button[class*="close"]');
if (!closeBtn) closeBtn = document.querySelector('.anticon-close');
if (!closeBtn) closeBtn = document.querySelector('.video-modal-close');
if (closeBtn) {
safeClick(closeBtn);
} else {
document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true}));
document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true}));
}
await sleep(3000);
}
if (stopRequested) {
log('info', '⏹️ 用户手动停止了续播');
} else {
log('success', '🎉 所有未完成视频已续播完毕!');
}
}
async function continueGenericIncomplete(items) {
totalVideos = items.length;
currentIndex = 0;
for (let i = 0; i < items.length; i++) {
if (stopRequested) break;
currentIndex = i + 1;
updateUI();
const { element, name } = items[i];
if (isPaused) {
log('info', '⏸️ 已暂停,等待继续...');
await new Promise(resolve => { pauseResolve = resolve; });
if (stopRequested) break;
log('info', '▶️ 继续学习');
}
log('info', `▶️ [续播] ${i + 1}/${items.length} - ${name}`);
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(config.scrollDelay);
safeClick(element);
await sleep(config.loadDelay);
await forcePlayVideo();
await waitForProcessToEnd(element, i + 1);
forceCloseModal();
await sleep(config.closeDelay);
}
if (stopRequested) {
log('info', '⏹️ 用户手动停止了续播');
} else {
log('success', '🎉 所有未完成视频已续播完毕!');
}
}
async function startSmartEduExclusiveAutoStudy() {
isRunning = true;
isPaused = false;
stopRequested = false;
currentIndex = 0;
updateUI();
log('info', '🚀 启动高等教育智慧教育平台专属自动学习');
console.log("🚀 启动智慧教育平台专属自动学习");
let videoItems = null;
for (let retry = 0; retry < 3; retry++) {
videoItems = findAllVideoItems();
if (videoItems.length > 0) break;
log('warning', `⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`);
console.log(`⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`);
await sleep(2000);
}
if (!videoItems || videoItems.length === 0) {
log('error', '❌ 经过3次重试仍未找到视频封面,请确认:1. 页面是否完全加载 2. 是否在课程播放页面');
console.error("❌ 经过3次重试仍未找到视频封面");
isRunning = false;
updateUI();
return;
}
totalVideos = videoItems.length;
let skippedCount = 0;
log('info', `共识别到 ${totalVideos} 个视频,准备开始按顺序播放...`);
console.log(`共识别到 ${videoItems.length} 个视频,准备开始无脑按顺序播放...`);
updateUI();
for (let i = 0; i < videoItems.length; i++) {
if (stopRequested) break;
currentIndex = i + 1;
updateUI();
const currentItem = videoItems[i];
// 检查视频是否已完成
const isCompleted = checkSmartEduItemCompleted(currentItem);
if (isCompleted) {
skippedCount++;
const progress = getItemProgressText(currentItem);
log('info', `⏭️ 第 ${i + 1} 个视频 [${progress || '100%'}] 已完成,自动跳过`);
continue;
}
// 识别中间进度
const progress = getItemProgressText(currentItem);
if (progress && !progress.includes('100%') && !progress.includes('已完成')) {
log('info', `📖 第 ${i + 1} 个视频进度 [${progress}],从上次位置继续播放`);
} else {
log('info', `📖 第 ${i + 1} 个视频未观看,开始播放`);
}
if (isPaused) {
log('info', '⏸️ 已暂停,等待继续...');
await new Promise(resolve => { pauseResolve = resolve; });
if (stopRequested) break;
log('info', '▶️ 继续学习');
}
log('info', `▶️ 正在打开第 ${currentIndex} 个视频...`);
console.log(`▶️ 正在打开第 ${i + 1} 个视频...`);
currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(1500);
safeClick(currentItem);
log('info', "⏳ 等待弹窗和视频加载...");
console.log("⏳ 等待 4 秒钟让弹窗和播放器完全渲染...");
await sleep(4000);
log('info', '🎬 正在激活视频播放...');
await forcePlayVideo();
log('info', "⏳ 正在等待视频播放完毕,请挂机...");
console.log("⏳ 正在等待视频播放完毕,请挂机...");
await watchSmartEduVideoUntilEnd(i + 1);
log('info', `✅ 第 ${currentIndex} 个视频结束,尝试关闭弹窗...`);
console.log(`✅ 第 ${i + 1} 个视频结束,尝试关闭弹窗...`);
let closeBtn = document.querySelector('button[class*="bg-white"][class*="h-10"]');
if (!closeBtn) closeBtn = document.querySelector('button[class*="close"]');
if (!closeBtn) closeBtn = document.querySelector('.anticon-close');
if (!closeBtn) closeBtn = document.querySelector('.video-modal-close');
if (closeBtn) {
log('info', "🎯 找到关闭按钮,点击关闭");
console.log("找到关闭按钮,点击关闭...");
safeClick(closeBtn);
} else {
log('info', "🎯 未找到任何关闭按钮,尝试按 ESC 关闭...");
console.log("未找到任何关闭按钮,尝试按 ESC 关闭...");
document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true}));
document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true}));
}
await sleep(3000);
}
if (stopRequested) {
log('info', '⏹️ 用户手动停止了学习');
} else {
if (skippedCount > 0) {
log('success', `🎉 视频轮询完毕!其中跳过已完成 ${skippedCount} 个,实际播放 ${totalVideos - skippedCount} 个`);
} else {
log('success', '🎉 所有界面的视频已全部轮询播放完毕!');
}
console.log("🎉 所有界面的视频已全部轮询播放完毕!");
}
}
function checkSmartEduItemCompleted(item) {
try {
// 优先检查 .process 元素,这是最准确的进度标识
const processEl = item.querySelector('.process');
if (processEl) {
const processText = (processEl.innerText || '').trim();
// 只有"已观看100%"才算完成
if (processText === '已观看100%' || processText.includes('已观看100%') || processText.includes('已完成')) {
return true;
}
// 其他任何进度(已观看10%、已观看50%等)都不算完成
return false;
}
// 兜底:检查整体文本
const allText = item.innerText || '';
if (allText.includes('已观看100%') || allText.includes('已完成')) {
return true;
}
} catch (e) {
console.warn("检查完成状态出错:", e);
}
return false;
}
// 获取视频的观看进度文本(用于日志展示)
function getItemProgressText(item) {
try {
// 优先从 .process 元素获取
const processEl = item.querySelector('.process');
if (processEl) {
const processText = (processEl.innerText || '').trim();
if (processText) return processText;
}
const allText = item.innerText || '';
// 尝试匹配"已观看XX%"格式
const match = allText.match(/已观看\s*\d+%/);
if (match) return match[0];
// 尝试匹配"XX%"格式
const pctMatch = allText.match(/\d+%/);
if (pctMatch) return pctMatch[0];
if (allText.includes('已完成')) return '已完成';
} catch (e) {}
return '';
}
// 智慧教育平台:获取视频名称
function getItemName(item, idx) {
try {
// 优先从 .tag-txt 获取(child-item 结构)
const tagTxt = item.querySelector('.tag-txt');
if (tagTxt) {
const title = tagTxt.getAttribute('title') || tagTxt.innerText;
if (title) return title.trim();
}
// 尝试 figcaption
const figcaption = item.querySelector('figcaption');
if (figcaption) return (figcaption.innerText || '').trim();
// 尝试 .title
const titleEl = item.querySelector('.title');
if (titleEl) return (titleEl.innerText || '').trim();
} catch (e) {}
return `视频 ${idx + 1}`;
}
// 智慧教育平台:查找所有视频项(兼容多种元素结构)
function findAllVideoItems() {
// 优先查找 .child-item(课程列表页的视频项)
let items = document.querySelectorAll('.child-item');
if (items.length > 0) {
// 过滤出包含视频标签的项
const videoItems = Array.from(items).filter(item => {
const tagEl = item.querySelector('.tag-video, .tag');
return tagEl !== null;
});
if (videoItems.length > 0) return videoItems;
}
// 其次查找 figure 元素(封面卡片模式)
items = document.querySelectorAll('figure[class*="cursor-pointer"]');
if (items.length > 0) return Array.from(items);
// 兜底查找 .name 元素
items = document.querySelectorAll('.name');
if (items.length > 0) return Array.from(items);
return [];
}
async function startIntegratedAutoStudy() {
isRunning = true;
isPaused = false;
stopRequested = false;
currentIndex = 0;
updateUI();
console.log("🚀 终极挂课脚本 V5.4.1 通用模式已启动!");
if (!isAllExpanded) {
log('info', '🔄 检测到未展开章节,正在自动展开...');
await expandAllChapters();
}
if (totalVideos === 0) {
log('error', '❌ 没有找到任何视频,请先点击"展开全部"按钮!');
isRunning = false;
updateUI();
return;
}
log('info', `✅ 共扫描到 ${totalVideos} 个视频节,准备开始自动过滤和播放...`);
for (let i = 0; i < totalVideos; i++) {
if (stopRequested) break;
currentIndex = i + 1;
updateUI();
const currentItem = videoItems[i];
const processElement = currentItem.querySelector('.process');
const processText = processElement ? (processElement.innerText || '').trim() : '';
// 只有"已观看100%"或"已完成"才算完成,其他进度(已观看10%等)不算完成
const isComplete = processText.includes('已观看100%') || processText.includes('已完成');
if (isComplete) {
log('info', `⏭️ 第 ${i + 1} 个视频进度为 [${processText}],自动跳过`);
continue;
}
// 识别中间进度,从上次位置继续
if (processText && processText.includes('%') && !processText.includes('100%')) {
log('info', `📖 第 ${i + 1} 个视频进度 [${processText}],从上次位置继续播放`);
}
if (isPaused) {
log('info', '⏸️ 已暂停,等待继续...');
await new Promise(resolve => { pauseResolve = resolve; });
if (stopRequested) break;
log('info', '▶️ 继续学习');
}
log('info', `▶️ 正在滚动并打开第 ${i + 1} 个未完成的视频...`);
currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(config.scrollDelay);
safeClick(currentItem);
await sleep(config.loadDelay);
log('info', "🎬 正在激活视频播放...");
await forcePlayVideo();
log('info', "⏳ 正在监测视频播放状态...");
await waitForProcessToEnd(currentItem, i + 1);
log('info', `☑️ 第 ${i + 1} 个视频流程结束,尝试强制关闭弹窗...`);
forceCloseModal();
await sleep(config.closeDelay);
}
if (stopRequested) {
log('info', '⏹️ 用户手动停止了学习');
} else {
log('success', '🎉 太棒了,所有任务脚本运行结束!');
}
}
async function forcePlayVideo() {
const video = document.querySelector('video');
if (video) {
video.muted = true;
// 不重置currentTime,从上次位置继续播放
const savedTime = video.currentTime;
await sleep(500);
// 确保没有因加载重置了进度
if (savedTime > 0 && video.currentTime < savedTime) {
log('info', `⏩ 恢复到上次播放位置 ${formatTime(savedTime)}`);
video.currentTime = savedTime;
}
}
const targets = [
document.querySelector('.video-play-img img'),
document.querySelector('.video-play-img'),
document.querySelector('.video-play'),
document.querySelector('.vjs-big-play-button'),
document.querySelector('button[class*="play"]'),
video
];
for (let target of targets) {
if (target && target.offsetParent !== null) {
log('info', `👉 尝试物理点击播放按钮...`);
safeClick(target);
await sleep(800);
if (video && !video.paused) {
log('success', '✅ 播放成功激活!');
return;
}
}
}
if (video && video.paused) {
log('info', '👉 物理点击无效,强制调用底层播放API...');
video.play().catch((err) => {
log('warning', `⚠️ 底层API被拦截: ${err.message},将继续轮询恢复`);
console.log("底层API被拦截:", err);
});
}
}
function togglePause() {
if (!isRunning) return;
isPaused = !isPaused;
updateUI();
if (!isPaused && pauseResolve) {
pauseResolve();
pauseResolve = null;
}
}
function stopAutoStudy() {
if (!isRunning) return;
stopRequested = true;
if (isPaused && pauseResolve) {
pauseResolve();
}
}
function toggleMinimize() {
const panel = document.getElementById('auto-study-panel');
panel.classList.toggle('minimized');
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function formatTime(seconds) {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
if (h > 0) return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
return `${m}:${String(s).padStart(2, '0')}`;
}
function randomSleep(min, max) {
const ms = Math.floor(Math.random() * (max - min + 1)) + min;
return sleep(ms);
}
function safeClick(element) {
if (!element) return;
try {
const realClick = new MouseEvent('click', {
bubbles: true,
cancelable: true,
clientX: element.getBoundingClientRect().left + element.offsetWidth / 2,
clientY: element.getBoundingClientRect().top + element.offsetHeight / 2
});
element.dispatchEvent(realClick);
console.log("✅ 安全点击成功:", element);
} catch (error) {
log('warning', `⚠️ 点击时出现小问题,已自动处理`);
console.error("点击错误:", error);
try { element.click(); } catch (e) {}
}
}
function forceCloseModal() {
const realClick = new MouseEvent('click', { bubbles: true, cancelable: true });
const spanIcon = document.querySelector('.anticon-close');
if (spanIcon && spanIcon.offsetParent !== null) {
log('info', "🎯 使用真实鼠标事件点击了关闭图标!");
spanIcon.dispatchEvent(realClick);
return;
}
const divContainer = document.querySelector('.video-modal-close');
if (divContainer && divContainer.offsetParent !== null) {
log('info', "🎯 使用真实鼠标事件点击了关闭容器!");
divContainer.dispatchEvent(realClick);
return;
}
const modalMask = document.querySelector('.video-modal-mask');
if (modalMask) {
log('info', "🎯 使用真实鼠标事件点击了遮罩层!");
modalMask.dispatchEvent(realClick);
return;
}
log('info', "🎯 尝试模拟按 ESC 键!");
document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true}));
document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true}));
}
function waitForProcessToEnd(itemNode, index) {
return new Promise((resolve) => {
let maxTimeout = 0;
let mainInterval = null;
let hasVideoEnded = false;
let endedConfirmCount = 0;
const ENDED_CONFIRM_THRESHOLD = 3;
const videoEl = document.querySelector('video');
if (videoEl) {
videoEl.muted = true;
videoEl.addEventListener('pause', async function() {
if (videoEl.currentTime < videoEl.duration - 1 && !isPaused && !stopRequested) {
log('warning', "⚡ 平台尝试暂停视频!自动重新激活播放...");
await forcePlayVideo();
}
});
}
function cleanupAndResolve() {
if (mainInterval) clearInterval(mainInterval);
resolve();
}
mainInterval = setInterval(async () => {
if (stopRequested) {
cleanupAndResolve();
return;
}
if (isPaused) return;
maxTimeout++;
const processNode = itemNode.querySelector('.process');
if (processNode && (processNode.innerText.includes('已观看100%') || processNode.innerText.includes('已完成'))) {
log('success', `✅ 第 ${index} 个视频系统进度已更新为完成!`);
cleanupAndResolve();
return;
}
const currentVideo = document.querySelector('video');
if (currentVideo && !hasVideoEnded) {
currentVideo.muted = true;
if (currentVideo.paused && currentVideo.currentTime < currentVideo.duration) {
log('warning', '⚡ 检测到视频暂停,强制恢复播放...');
await forcePlayVideo();
}
currentVideo.play().catch(() => {});
if (currentVideo.ended || (currentVideo.duration > 0 && currentVideo.duration - currentVideo.currentTime <= 1)) {
endedConfirmCount++;
if (endedConfirmCount >= ENDED_CONFIRM_THRESHOLD) {
hasVideoEnded = true;
log('warning', `⚠️ 第 ${index} 个视频本体已播完(连续${ENDED_CONFIRM_THRESHOLD}次确认),跳过平台服务器响应判定。`);
cleanupAndResolve();
return;
}
} else {
endedConfirmCount = 0;
}
}
if (maxTimeout > config.maxTimeout) {
log('warning', `🚨 第 ${index} 个视频监测超时(可能服务器504),强行跳过。`);
cleanupAndResolve();
}
}, config.checkInterval);
});
}
function watchSmartEduVideoUntilEnd(index) {
return new Promise((resolve) => {
let notFoundCount = 0;
let endedConfirmCount = 0;
const ENDED_CONFIRM_THRESHOLD = 3;
const checkInterval = setInterval(async () => {
if (stopRequested) {
clearInterval(checkInterval);
resolve();
return;
}
if (isPaused) return;
const videoEl = document.querySelector('video');
if (videoEl) {
notFoundCount = 0;
videoEl.muted = true;
if (videoEl.paused && videoEl.currentTime < videoEl.duration) {
log('warning', '⚡ 检测到视频暂停,强制恢复播放...');
// 优先点击播放按钮,再兜底API调用
const playBtn = document.querySelector('.video-play-img img') ||
document.querySelector('.video-play-img') ||
document.querySelector('.vjs-big-play-button') ||
document.querySelector('button[class*="play"]');
if (playBtn && playBtn.offsetParent !== null) {
safeClick(playBtn);
await sleep(500);
if (!videoEl.paused) {
return; // 播放成功,跳过本次检查
}
}
videoEl.play().catch(() => console.log("尝试自动播放被拦截"));
}
// 连续多次确认视频已结束,避免误判
if (videoEl.ended || (videoEl.duration > 0 && videoEl.currentTime >= videoEl.duration - 1)) {
endedConfirmCount++;
if (endedConfirmCount >= ENDED_CONFIRM_THRESHOLD) {
log('success', `✅ 第 ${index} 个视频已确认播放完毕(连续${ENDED_CONFIRM_THRESHOLD}次检测)`);
clearInterval(checkInterval);
resolve();
}
} else {
endedConfirmCount = 0;
}
} else {
notFoundCount++;
if (notFoundCount >= 5) {
log('warning', `⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`);
console.warn(`⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`);
clearInterval(checkInterval);
resolve();
}
}
}, 3000);
});
}
function log(type, message) {
try {
const container = document.getElementById('log-container');
const item = document.createElement('div');
item.className = `log-item ${type}`;
item.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
container.appendChild(item);
container.scrollTop = container.scrollHeight;
} catch (error) {
console.log(message);
}
}
function clearLog() {
document.getElementById('log-container').innerHTML = '';
}
function updateUI() {
try {
const statusText = document.getElementById('status-text');
const progressText = document.getElementById('progress-text');
const progressFill = document.getElementById('progress-fill');
const expandBtn = document.getElementById('expand-btn');
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
const stopBtn = document.getElementById('stop-btn');
if (isRunning) {
if (isPaused) {
statusText.textContent = '已暂停';
statusText.className = 'status-value paused';
pauseBtn.textContent = '继续';
} else {
statusText.textContent = '学习中';
statusText.className = 'status-value running';
pauseBtn.textContent = '暂停';
}
} else {
statusText.textContent = '已停止';
statusText.className = 'status-value stopped';
}
progressText.textContent = `${currentIndex} / ${totalVideos}`;
const progress = totalVideos > 0 ? (currentIndex / totalVideos) * 100 : 0;
progressFill.style.width = `${progress}%`;
expandBtn.disabled = isRunning || isSmartEduPlatform;
startBtn.disabled = isRunning;
pauseBtn.disabled = !isRunning;
stopBtn.disabled = !isRunning;
const detectBtn = document.getElementById('detect-btn');
const continueBtn = document.getElementById('continue-incomplete-btn');
if (detectBtn) detectBtn.disabled = isRunning;
if (continueBtn) {
if (isRunning) {
continueBtn.disabled = true;
}
// 不运行时保留之前的启用/禁用状态(由detectCourseProgress设置)
}
} catch (error) {
console.error("更新UI错误:", error);
}
}
// 全局视频暂停监控:无论何时视频被暂停,自动恢复
function setupVideoPauseGuard() {
// 使用MutationObserver持续监听新出现的video元素
const observer = new MutationObserver(() => {
const videos = document.querySelectorAll('video');
videos.forEach(video => {
if (video._pauseGuardInstalled) return;
video._pauseGuardInstalled = true;
// 拦截video.pause()调用
const originalPause = video.pause.bind(video);
video.pause = function() {
// 允许用户手动暂停(isPaused)或脚本停止(stopRequested)
if (isRunning && !isPaused && !stopRequested) {
console.log("🛡️ 拦截了平台对video.pause()的调用,自动恢复播放");
return; // 直接忽略暂停请求
}
return originalPause();
};
// 监听pause事件,防止非脚本触发的暂停
video.addEventListener('pause', function() {
if (isRunning && !isPaused && !stopRequested && video.currentTime < video.duration - 1) {
console.log("🛡️ 检测到视频被暂停,300ms后自动恢复...");
setTimeout(() => {
if (isRunning && !isPaused && !stopRequested) {
video.play().catch(() => {});
}
}, 300);
}
});
// 监听视频被移除或替换后重新挂载守卫
video.addEventListener('loadstart', function() {
video._pauseGuardInstalled = false;
// 延迟重新安装,避免在加载过程中干扰
setTimeout(() => setupVideoPauseGuard(), 1000);
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
function init() {
console.log("🔧 开始初始化脚本...");
isSmartEduPlatform = window.location.href.includes('higher.smartedu.cn/course/');
console.log("📱 平台检测结果:", isSmartEduPlatform ? "智慧教育平台" : "通用平台");
setupVideoPauseGuard();
if (document.readyState === 'complete') {
createPanel();
} else {
window.addEventListener('load', createPanel);
}
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(init, 1000);
} else {
window.addEventListener('DOMContentLoaded', () => {
setTimeout(init, 1000);
});
}
})();