// ==UserScript== // @name 上海开放大学视频智能学习助手 // @namespace http://tampermonkey.net/ // @version 1.1.0 // @description 上海开放大学学习平台专用,智能自动播放、进度记忆、防掉线、学习统计、专注模式、键盘快捷键 // @author lakay666 // @match *://*.shtvu.edu.cn/* // @match *://elearning.shtvu.edu.cn/* // @match *://study.shtvu.edu.cn/* // @match *://course.shtvu.edu.cn/* // @match *://mooc.shtvu.edu.cn/* // @match *://learning.shou.org.cn/* // @match *://*.shou.org.cn/* // @icon https://www.google.com/s2/favicons?sz=64&domain=shtvu.edu.cn // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant unsafeWindow // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // ==================== 配置管理 ==================== const CONFIG = { autoPlay: GM_getValue('autoPlay', true), autoMute: GM_getValue('autoMute', true), autoNext: GM_getValue('autoNext', true), showContact: GM_getValue('showContact', true), customSpeed: parseFloat(GM_getValue('customSpeed', 1.0)), skipCompleted: GM_getValue('skipCompleted', true), disableMonitor: GM_getValue('disableMonitor', false), rememberProgress: GM_getValue('rememberProgress', true), // 记忆播放进度 keyboardShortcuts: GM_getValue('keyboardShortcuts', true), // 键盘快捷键 antiOffline: GM_getValue('antiOffline', true), // 防掉线检测 studyStats: GM_getValue('studyStats', true), // 学习统计 smartSpeed: GM_getValue('smartSpeed', false), // 智能变速 focusMode: GM_getValue('focusMode', false), // 专注模式 }; // 学习统计数据 let studyData = { todayWatchTime: parseInt(GM_getValue('todayWatchTime', 0)), totalWatchTime: parseInt(GM_getValue('totalWatchTime', 0)), videosCompleted: parseInt(GM_getValue('videosCompleted', 0)), lastStudyDate: GM_getValue('lastStudyDate', new Date().toDateString()), currentSessionTime: 0, sessionStart: Date.now() }; // 重置每日统计 if (studyData.lastStudyDate !== new Date().toDateString()) { studyData.todayWatchTime = 0; studyData.lastStudyDate = new Date().toDateString(); saveStudyData(); } function saveConfig() { try { Object.keys(CONFIG).forEach(key => { GM_setValue(key, CONFIG[key]); }); } catch (e) { console.error('[白白助手] 保存配置失败:', e); } } function saveStudyData() { try { GM_setValue('todayWatchTime', studyData.todayWatchTime); GM_setValue('totalWatchTime', studyData.totalWatchTime); GM_setValue('videosCompleted', studyData.videosCompleted); GM_setValue('lastStudyDate', studyData.lastStudyDate); } catch (e) { console.error('[白白助手] 保存学习数据失败:', e); } } // ==================== 工具函数 ==================== const $ = (selector, context = document) => context.querySelector(selector); const $$ = (selector, context = document) => Array.from(context.querySelectorAll(selector)); function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // 格式化时间 function formatTime(seconds) { const hrs = Math.floor(seconds / 3600); const mins = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; if (hrs > 0) return `${hrs}小时${mins}分`; return `${mins}分${secs}秒`; } // ==================== 通知系统 ==================== function showNotification(message, type = 'info', duration = 3000) { try { if (!document.body) return; const notification = document.createElement('div'); notification.className = `baibai-notification notification-${type}`; const icons = { success: '✓', error: '✗', info: 'ℹ', warning: '⚠' }; notification.innerHTML = ` ${icons[type] || 'ℹ'} ${message} `; document.body.appendChild(notification); requestAnimationFrame(() => { notification.classList.add('show'); }); setTimeout(() => { notification.classList.add('fade-out'); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, duration); } catch (e) { console.error('[白白助手] 显示通知失败:', e); } } // ==================== 视频进度记忆 ==================== const ProgressMemory = { getKey(video) { const courseId = this.getCourseId(); const videoSrc = video.src || video.currentSrc || 'unknown'; return `progress_${courseId}_${videoSrc}`; }, getCourseId() { const match = location.href.match(/course[_-]?id[=\/](\d+)/i) || location.href.match(/[?&]id=(\d+)/); if (match) return match[1]; const courseTitle = $('.course-title, .lesson-title, h1'); if (courseTitle) return courseTitle.textContent.trim().slice(0, 50); return location.pathname; }, save(video) { if (!CONFIG.rememberProgress || !video.duration) return; try { const key = this.getKey(video); const data = { currentTime: video.currentTime, duration: video.duration, timestamp: Date.now(), url: location.href }; GM_setValue(key, JSON.stringify(data)); } catch (e) { console.error('[白白助手] 保存进度失败:', e); } }, load(video) { if (!CONFIG.rememberProgress) return null; try { const key = this.getKey(video); const data = GM_getValue(key); if (!data) return null; const parsed = JSON.parse(data); if (Date.now() - parsed.timestamp > 7 * 24 * 60 * 60 * 1000) { GM_deleteValue(key); return null; } if (parsed.url && parsed.url.split('?')[0] !== location.href.split('?')[0]) { return null; } return parsed; } catch (e) { console.error('[白白助手] 读取进度失败:', e); return null; } }, clear(video) { try { const key = this.getKey(video); GM_deleteValue(key); } catch (e) { console.error('[白白助手] 清除进度失败:', e); } } }; // ==================== 核心视频处理 ==================== const VideoHandler = { processedVideos: new WeakSet(), init() { this.observeVideos(); this.startProgressTracking(); }, observeVideos() { const observer = new MutationObserver(() => { const videos = $$('video'); videos.forEach(video => this.processVideo(video)); }); if (document.body) { observer.observe(document.body, { childList: true, subtree: true }); } $$('video').forEach(video => this.processVideo(video)); }, processVideo(video) { if (this.processedVideos.has(video)) return; this.processedVideos.add(video); if (video.readyState < 1) { video.addEventListener('loadedmetadata', () => this.setupVideo(video), { once: true }); } else { this.setupVideo(video); } }, setupVideo(video) { try { if (CONFIG.rememberProgress) { const progress = ProgressMemory.load(video); if (progress && progress.currentTime > 10 && progress.currentTime < progress.duration - 10) { video.currentTime = progress.currentTime; showNotification(`已恢复上次进度: ${formatTime(Math.floor(progress.currentTime))}`, 'info', 2000); } } if (CONFIG.autoMute) { video.muted = true; } this.applySpeed(video); if (CONFIG.autoPlay && video.paused) { this.attemptPlay(video); } this.attachEventListeners(video); } catch (e) { console.error('[白白助手] 设置视频失败:', e); } }, attemptPlay(video) { const playPromise = video.play(); if (playPromise !== undefined) { playPromise.catch(err => { console.log('[白白助手] 自动播放被阻止,尝试交互模拟:', err); this.simulateInteraction(video); }); } }, simulateInteraction(video) { const events = ['mousedown', 'mouseup', 'click']; events.forEach((type, index) => { setTimeout(() => { const event = new MouseEvent(type, { bubbles: true, cancelable: true, view: unsafeWindow }); video.dispatchEvent(event); }, index * 50); }); setTimeout(() => { video.play().catch(() => { showNotification('请点击页面任意位置后,视频将自动播放', 'warning'); }); }, 200); }, attachEventListeners(video) { video.addEventListener('timeupdate', throttle(() => { if (video.currentTime > 0 && video.currentTime % 5 < 0.5) { ProgressMemory.save(video); this.updateStudyTime(); } }, 1000)); video.addEventListener('ended', () => { ProgressMemory.clear(video); studyData.videosCompleted++; saveStudyData(); showNotification('🎉 视频播放完成!', 'success'); if (CONFIG.autoNext) { setTimeout(() => Navigation.next(), 2000); } }); video.addEventListener('error', () => { showNotification('视频加载出错,尝试刷新...', 'error'); setTimeout(() => location.reload(), 3000); }); if (CONFIG.disableMonitor) { video.addEventListener('pause', () => { if (CONFIG.autoPlay && video.currentTime > 0 && video.currentTime < video.duration - 1) { setTimeout(() => video.play().catch(() => {}), 1000); } }); } }, applySpeed(video) { if (!video) return; try { if (CONFIG.smartSpeed && video.duration) { if (video.duration < 300) video.playbackRate = 1.5; else if (video.duration > 1800) video.playbackRate = 1.25; else video.playbackRate = CONFIG.customSpeed; } else { video.playbackRate = CONFIG.customSpeed; } } catch (e) { console.error('[白白助手] 设置速度失败:', e); } }, updateStudyTime() { const now = Date.now(); const delta = Math.floor((now - studyData.sessionStart) / 1000); if (delta > 0) { studyData.currentSessionTime += delta; studyData.todayWatchTime += delta; studyData.totalWatchTime += delta; studyData.sessionStart = now; saveStudyData(); } }, startProgressTracking() { setInterval(() => this.updateStudyTime(), 30000); }, getAllVideos() { return $$('video'); } }; // ==================== 导航控制 ==================== const Navigation = { next() { const selectors = [ '.next-btn', '.next-button', '.btn-next', '.next-step', '.next-chapter', '.next-lesson', '.shtvu-next', '[class*="next"][class*="btn"]', '[class*="next"][class*="button"]', '.task-next', '.lesson-next', '.course-next', '.finish-button', '.complete-button', '.continue-button' ]; for (const selector of selectors) { const btn = $(selector); if (btn && this.isVisible(btn)) { btn.scrollIntoView({ behavior: 'smooth', block: 'center' }); setTimeout(() => { btn.click(); showNotification('已自动跳转到下一节', 'success'); }, 500); return true; } } return this.findNextUncompleted(); }, findNextUncompleted() { const itemSelectors = [ '.task-item', '.lesson-item', '.chapter-item', '.course-item', '.content-item', '.video-item', '.list-item', '.menu-item', '.nav-item' ]; for (const selector of itemSelectors) { const items = $$(selector); for (const item of items) { const isCompleted = item.querySelector('.completed, .finished, .success, .check, [class*="finish"], [class*="complete"]'); const isActive = item.classList.contains('active') || item.classList.contains('current'); if (!isCompleted && !isActive && this.isVisible(item)) { item.scrollIntoView({ behavior: 'smooth', block: 'center' }); setTimeout(() => { item.click(); showNotification('已跳转到下一个未完成任务', 'success'); }, 500); return true; } } } showNotification('未找到可跳转的下一节内容', 'info'); return false; }, isVisible(el) { return el && el.offsetParent !== null && getComputedStyle(el).display !== 'none' && getComputedStyle(el).visibility !== 'hidden'; } }; // ==================== 防掉线系统 ==================== const AntiOffline = { interval: null, init() { if (!CONFIG.antiOffline) return; this.interval = setInterval(() => { this.simulateActivity(); }, 60000 + Math.random() * 30000); if (unsafeWindow.document) { Object.defineProperty(unsafeWindow.document, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(unsafeWindow.document, 'visibilityState', { get: () => 'visible', configurable: true }); } }, simulateActivity() { try { const event = new MouseEvent('mousemove', { bubbles: true, cancelable: true, clientX: Math.random() * window.innerWidth, clientY: Math.random() * window.innerHeight }); document.dispatchEvent(event); const keyEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Shift', code: 'ShiftLeft' }); document.dispatchEvent(keyEvent); VideoHandler.getAllVideos().forEach(video => { if (video.paused && CONFIG.autoPlay) { VideoHandler.attemptPlay(video); } }); console.log('[白白助手] 防掉线心跳'); } catch (e) { console.error('[白白助手] 防掉线失败:', e); } }, stop() { if (this.interval) { clearInterval(this.interval); this.interval = null; } } }; // ==================== 专注模式 ==================== const FocusMode = { toggle() { CONFIG.focusMode = !CONFIG.focusMode; saveConfig(); this.apply(); }, apply() { const style = $('#baibai-focus-mode') || document.createElement('style'); style.id = 'baibai-focus-mode'; if (CONFIG.focusMode) { style.textContent = ` .sidebar, .nav-menu, .header, .footer, .chat-box, .recommend, .related-courses, .advertisement, .course-forum, .student-list, .notification-center { display: none !important; } .main-content, .video-container, .course-content { width: 100% !important; max-width: 100% !important; } `; showNotification('专注模式已开启', 'success'); } else { style.textContent = ''; } if (!style.parentNode) document.head.appendChild(style); } }; // ==================== 键盘快捷键 ==================== const Shortcuts = { init() { if (!CONFIG.keyboardShortcuts) return; document.addEventListener('keydown', (e) => { if (e.target.matches('input, textarea, [contenteditable]')) return; const video = $('video'); if (!video) return; switch(e.key.toLowerCase()) { case ' ': e.preventDefault(); video.paused ? video.play() : video.pause(); break; case 'arrowright': e.preventDefault(); video.currentTime += 10; showNotification(`快进 10秒`, 'info', 1000); break; case 'arrowleft': e.preventDefault(); video.currentTime -= 10; showNotification(`后退 10秒`, 'info', 1000); break; case 'arrowup': e.preventDefault(); video.volume = Math.min(1, video.volume + 0.1); showNotification(`音量: ${Math.round(video.volume * 100)}%`, 'info', 1000); break; case 'arrowdown': e.preventDefault(); video.volume = Math.max(0, video.volume - 0.1); showNotification(`音量: ${Math.round(video.volume * 100)}%`, 'info', 1000); break; case 'm': e.preventDefault(); video.muted = !video.muted; showNotification(video.muted ? '已静音' : '已取消静音', 'info', 1000); break; case 'n': e.preventDefault(); Navigation.next(); break; case 's': if (e.ctrlKey || e.metaKey) return; e.preventDefault(); CONFIG.customSpeed = CONFIG.customSpeed >= 2 ? 0.5 : CONFIG.customSpeed + 0.25; VideoHandler.applySpeed(video); showNotification(`播放速度: ${CONFIG.customSpeed}x`, 'info', 1000); break; } }); } }; // ==================== 控制面板 ==================== const ControlPanel = { create() { if ($('#baibai-auto-play-panel')) return; const panel = document.createElement('div'); panel.id = 'baibai-auto-play-panel'; panel.innerHTML = this.getHTML(); document.body.appendChild(panel); this.attachEvents(panel); this.updateStats(); }, getHTML() { const createToggle = (id, label, checked) => `
`; const createSelect = (id, label, options, value) => `
`; return `

🎓 白白学习助手

⚡ 核心功能

${createToggle('baibai-auto-play', '自动播放视频', CONFIG.autoPlay)} ${createToggle('baibai-auto-mute', '自动静音', CONFIG.autoMute)} ${createToggle('baibai-auto-next', '自动下一节', CONFIG.autoNext)} ${createToggle('baibai-skip-completed', '跳过已完成', CONFIG.skipCompleted)} ${createToggle('baibai-remember-progress', '记忆播放进度', CONFIG.rememberProgress)} ${createToggle('baibai-smart-speed', '智能变速', CONFIG.smartSpeed)} ${createToggle('baibai-anti-offline', '防掉线保护', CONFIG.antiOffline)} ${createToggle('baibai-focus-mode', '专注模式', CONFIG.focusMode)} ${createToggle('baibai-keyboard-shortcuts', '键盘快捷键', CONFIG.keyboardShortcuts)}

⚙️ 播放设置

${createSelect('baibai-playback-speed', '播放速度:', [ {value: 0.5, label: '0.5x'}, {value: 1.0, label: '1.0x'}, {value: 1.25, label: '1.25x'}, {value: 1.5, label: '1.5x'}, {value: 2.0, label: '2.0x'}, {value: 3.0, label: '3.0x'}, {value: 4.0, label: '4.0x'} ], CONFIG.customSpeed)}

📊 学习统计

0 今日学习
0 累计学习
0 完成视频
${CONFIG.showContact ? `

💬 制作人: lakay666

` : ''}
`; }, attachEvents(panel) { $('#baibai-close-panel').onclick = () => { panel.style.display = 'none'; }; const toggles = { 'baibai-auto-play': 'autoPlay', 'baibai-auto-mute': 'autoMute', 'baibai-auto-next': 'autoNext', 'baibai-skip-completed': 'skipCompleted', 'baibai-remember-progress': 'rememberProgress', 'baibai-smart-speed': 'smartSpeed', 'baibai-anti-offline': 'antiOffline', 'baibai-keyboard-shortcuts': 'keyboardShortcuts' }; Object.entries(toggles).forEach(([id, key]) => { const el = $(`#${id}`); if (el) { el.addEventListener('change', (e) => { CONFIG[key] = e.target.checked; saveConfig(); if (key === 'antiOffline') { e.target.checked ? AntiOffline.init() : AntiOffline.stop(); } showNotification(`${e.target.nextElementSibling.textContent} ${e.target.checked ? '已开启' : '已关闭'}`, 'success'); }); } }); const focusToggle = $('#baibai-focus-mode'); if (focusToggle) { focusToggle.addEventListener('change', (e) => { CONFIG.focusMode = e.target.checked; saveConfig(); FocusMode.apply(); }); } const speedSelect = $('#baibai-playback-speed'); if (speedSelect) { speedSelect.addEventListener('change', (e) => { CONFIG.customSpeed = parseFloat(e.target.value); saveConfig(); VideoHandler.getAllVideos().forEach(v => VideoHandler.applySpeed(v)); showNotification(`播放速度已设置为 ${CONFIG.customSpeed}x`, 'success'); }); } const refreshBtn = $('#baibai-refresh-video'); if (refreshBtn) { refreshBtn.addEventListener('click', () => { VideoHandler.getAllVideos().forEach(v => { VideoHandler.processedVideos.delete(v); VideoHandler.processVideo(v); }); showNotification('视频检测已刷新', 'success'); }); } const resetBtn = $('#baibai-reset-progress'); if (resetBtn) { resetBtn.addEventListener('click', () => { if (confirm('确定要清除所有保存的学习进度吗?')) { const keys = GM_listValues(); keys.forEach(key => { if (key.startsWith('progress_')) GM_deleteValue(key); }); showNotification('所有进度已重置', 'success'); } }); } const toggleContact = $('#baibai-toggle-contact'); if (toggleContact) { toggleContact.addEventListener('click', () => { CONFIG.showContact = !CONFIG.showContact; saveConfig(); this.create(); }); } }, updateStats() { const updateValue = (id, value) => { const el = $(id); if (el) el.textContent = value; }; updateValue('#stat-today', formatTime(studyData.todayWatchTime)); updateValue('#stat-total', formatTime(studyData.totalWatchTime)); updateValue('#stat-videos', studyData.videosCompleted); }, show() { let panel = $('#baibai-auto-play-panel'); if (!panel) { this.create(); panel = $('#baibai-auto-play-panel'); } panel.style.display = 'block'; this.updateStats(); } }; // ==================== 悬浮按钮 ==================== const FloatingButton = { create() { if ($('#baibai-floating-btn')) return; const btn = document.createElement('div'); btn.id = 'baibai-floating-btn'; btn.innerHTML = '🎓'; btn.title = '打开白白助手面板'; btn.onclick = () => ControlPanel.show(); document.body.appendChild(btn); } }; // ==================== 样式定义 ==================== const Styles = { inject() { GM_addStyle(` #baibai-floating-btn { position: fixed; right: 30px; bottom: 30px; width: 56px; height: 56px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; cursor: pointer; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); z-index: 9999; transition: all 0.3s ease; user-select: none; } #baibai-floating-btn:hover { transform: scale(1.1) rotate(10deg); box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); } #baibai-auto-play-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; max-height: 85vh; background: #1a1a2e; border-radius: 16px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif; color: #eee; overflow: hidden; display: none; border: 1px solid rgba(255, 255, 255, 0.1); } .baibai-panel-header { display: flex; justify-content: space-between; align-items: center; padding: 20px 24px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } .baibai-panel-header h3 { margin: 0; font-size: 18px; font-weight: 600; display: flex; align-items: center; gap: 8px; } #baibai-close-panel { background: rgba(255, 255, 255, 0.2); border: none; color: white; font-size: 20px; cursor: pointer; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } #baibai-close-panel:hover { background: rgba(255, 255, 255, 0.3); transform: rotate(90deg); } .baibai-panel-body { padding: 20px; overflow-y: auto; max-height: calc(85vh - 70px); } .baibai-section { margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid rgba(255, 255, 255, 0.05); } .baibai-section:last-of-type { border-bottom: none; } .baibai-section h4 { margin: 0 0 12px 0; font-size: 13px; color: #8892b0; text-transform: uppercase; letter-spacing: 1px; } .baibai-toggle { display: flex; align-items: center; cursor: pointer; position: relative; padding: 8px 0; } .baibai-toggle input { opacity: 0; width: 0; height: 0; } .baibai-toggle-slider { position: relative; width: 44px; height: 24px; background: #4a5568; border-radius: 24px; transition: 0.3s; margin-right: 12px; flex-shrink: 0; } .baibai-toggle-slider:before { content: ""; position: absolute; height: 18px; width: 18px; left: 3px; bottom: 3px; background: white; border-radius: 50%; transition: 0.3s; } .baibai-toggle input:checked + .baibai-toggle-slider { background: #667eea; } .baibai-toggle input:checked + .baibai-toggle-slider:before { transform: translateX(20px); } .baibai-toggle-label { font-size: 14px; color: #e2e8f0; } .baibai-select-label { display: flex; align-items: center; justify-content: space-between; font-size: 14px; color: #e2e8f0; } .baibai-select-label select { background: #2d3748; color: white; border: 1px solid #4a5568; border-radius: 6px; padding: 6px 12px; font-size: 14px; cursor: pointer; } .baibai-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 8px; } .stat-item { background: rgba(102, 126, 234, 0.1); padding: 12px; border-radius: 8px; text-align: center; border: 1px solid rgba(102, 126, 234, 0.2); } .stat-value { display: display: block; font-size: 16px; font-weight: 700; color: #667eea; margin-bottom: 4px; } .stat-label { font-size: 12px; color: #8892b0; } .baibai-contact-info { margin: 16px 0; padding: 12px; background: rgba(102, 126, 234, 0.1); border-radius: 8px; text-align: center; border: 1px solid rgba(102, 126, 234, 0.2); } .baibai-contact-info p { margin: 0; font-size: 13px; color: #a0aec0; } .baibai-panel-footer { display: flex; gap: 8px; margin-top: 16px; } .baibai-panel-footer button { flex: 1; padding: 10px; background: #2d3748; color: #e2e8f0; border: 1px solid #4a5568; border-radius: 6px; cursor: pointer; font-size: 13px; transition: all 0.2s; } .baibai-panel-footer button:hover { background: #4a5568; border-color: #667eea; } .baibai-panel-footer button:last-child { background: #667eea; border-color: #667eea; color: white; } .baibai-panel-footer button:last-child:hover { background: #764ba2; } .baibai-notification { position: fixed; top: 20px; right: 20px; padding: 14px 20px; background: #2d3748; color: white; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); z-index: 10001; font-size: 14px; display: flex; align-items: center; gap: 10px; transform: translateX(120%); transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55); border: 1px solid rgba(255, 255, 255, 0.1); max-width: 300px; } .baibai-notification.show { transform: translateX(0); } .notification-icon { font-size: 18px; } .notification-success { border-left: 4px solid #48bb78; } .notification-error { border-left: 4px solid #f56565; } .notification-info { border-left: 4px solid #4299e1; } .notification-warning { border-left: 4px solid #ed8936; } .baibai-notification.fade-out { opacity: 0; transform: translateX(20px); transition: all 0.3s ease; } .baibai-panel-body::-webkit-scrollbar { width: 6px; } .baibai-panel-body::-webkit-scrollbar-track { background: transparent; } .baibai-panel-body::-webkit-scrollbar-thumb { background: #4a5568; border-radius: 3px; } `); } }; // ==================== 初始化 ==================== function init() { try { console.log('[白白助手] 初始化中...'); Styles.inject(); GM_registerMenuCommand('🎓 打开白白助手面板', () => ControlPanel.show()); GM_registerMenuCommand('▶️ 立即播放视频', () => { VideoHandler.getAllVideos().forEach(v => VideoHandler.attemptPlay(v)); }); GM_registerMenuCommand('⏭️ 跳转到下一节', () => Navigation.next()); GM_registerMenuCommand('🎯 切换专注模式', () => FocusMode.toggle()); VideoHandler.init(); Shortcuts.init(); if (CONFIG.antiOffline) AntiOffline.init(); if (CONFIG.focusMode) FocusMode.apply(); setTimeout(() => FloatingButton.create(), 1000); setInterval(() => ControlPanel.updateStats(), 5000); document.addEventListener('visibilitychange', () => { if (!document.hidden && CONFIG.autoPlay) { setTimeout(() => { VideoHandler.getAllVideos().forEach(v => { if (v.paused) VideoHandler.attemptPlay(v); }); }, 1000); } }); console.log('[白白助手] 初始化完成 v1.1.0'); showNotification('白白助手已启动', 'success'); } catch (e) { console.error('[白白助手] 初始化失败:', e); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();