// ==UserScript== // @name 医博士自动看课 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 医博士自动看课工具,自动连续播放课程视频并保存进度 // @author You // @match https://www.yiboshi.com/* // @match https://yiboshi.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @run-at document-idle // @noframes // ==/UserScript== (function() { 'use strict'; const STORAGE_KEY = 'yiboshi_auto_play_progress'; const POLL_INTERVAL = 15000; let isPlaying = false; let currentVideo = null; let courseList = []; let currentCourseIndex = 0; function getProgress() { try { const data = GM_getValue(STORAGE_KEY, '{}'); return JSON.parse(data); } catch (e) { return {}; } } function saveProgress(courseId, watchedTime) { const progress = getProgress(); progress[courseId] = watchedTime; GM_setValue(STORAGE_KEY, JSON.stringify(progress)); console.log(`进度已保存: 课程${courseId}, 时间${watchedTime}秒`); } function getLastWatchedTime(courseId) { const progress = getProgress(); return progress[courseId] || 0; } function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 25px; background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : '#2196F3'}; color: white; border-radius: 8px; font-size: 14px; z-index: 10000; box-shadow: 0 4px 12px rgba(0,0,0,0.3); transition: opacity 0.3s; `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => notification.remove(), 300); }, 3000); } function addControlPanel() { const panel = document.createElement('div'); panel.id = 'yiboshi-auto-panel'; panel.style.cssText = ` position: fixed; top: 10px; right: 10px; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; padding: 20px; border-radius: 12px; z-index: 9999; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; box-shadow: 0 8px 32px rgba(0,0,0,0.3); min-width: 300px; `; panel.innerHTML = `
🩺 医博士自动看课
状态: 等待开始
当前课程: 未知
观看进度: 0%
找到 0 个视频
提示: 开始前请确保在课程列表页面
`; document.body.appendChild(panel); document.getElementById('yiboshi-start').addEventListener('click', startAutoPlay); document.getElementById('yiboshi-stop').addEventListener('click', stopAutoPlay); document.getElementById('yiboshi-reset').addEventListener('click', resetProgress); GM_addStyle(` #yiboshi-start:hover { background: #45a049 !important; transform: scale(1.02); } #yiboshi-stop:hover { background: #da190b !important; transform: scale(1.02); } #yiboshi-reset:hover { background: #e68900 !important; } `); } function updateStatus(status) { const statusElem = document.getElementById('yiboshi-status'); if (statusElem) { statusElem.textContent = `状态: ${status}`; } } function updateCurrentCourse(courseName) { const courseElem = document.getElementById('yiboshi-current'); if (courseElem) { courseElem.textContent = `当前课程: ${courseName}`; } } function updateProgress(percent) { const progressElem = document.getElementById('yiboshi-progress'); if (progressElem) { progressElem.textContent = `观看进度: ${percent}%`; } } function updateCourseList(count) { const coursesElem = document.getElementById('yiboshi-courses'); if (coursesElem) { coursesElem.textContent = `找到 ${count} 个视频`; } } function findCourseLinks() { const links = []; const courseSelectors = [ '.course-list .course-item', '.lesson-item', '.video-item', 'tr[class*="course"]', 'tr[class*="lesson"]', '.list-group-item', '.card' ]; courseSelectors.forEach(selector => { const items = document.querySelectorAll(selector); items.forEach((item, index) => { try { const linkElem = item.querySelector('a'); if (linkElem) { const href = linkElem.href; const title = linkElem.textContent.trim() || item.textContent.trim(); if (href && title && (title.includes('视频') || title.includes('课程') || title.includes('播放') || title.includes('学习'))) { links.push({ href, title, index }); } } } catch (e) { console.log('查找课程链接失败:', e); } }); }); if (links.length === 0) { const allLinks = document.querySelectorAll('a'); allLinks.forEach((link, index) => { const href = link.href; const text = link.textContent.trim(); if (href && text && (text.includes('视频') || text.includes('课程') || text.includes('播放') || text.includes('学习'))) { links.push({ href, title: text, index }); } }); } return links; } function findVideo() { const videos = document.querySelectorAll('video'); if (videos.length > 0) { return videos[0]; } const iframes = document.querySelectorAll('iframe'); for (const iframe of iframes) { const src = iframe.src || iframe.getAttribute('data-src'); if (src && (src.includes('video') || src.includes('player') || src.includes('embed'))) { return iframe; } } const videoPlayers = document.querySelectorAll('[class*="player"], [class*="video"]'); for (const player of videoPlayers) { const video = player.querySelector('video'); if (video) { return video; } } return null; } function getCourseId(url) { const match = url.match(/\/(\d+)$/) || url.match(/id=(\d+)/) || url.match(/\/(\d+)\//) || url.match(/lesson\/(\d+)/); return match ? match[1] : url; } function playVideo(video) { return new Promise((resolve, reject) => { if (!video) { reject(new Error('未找到视频元素')); return; } try { if (video.tagName === 'VIDEO') { const courseId = getCourseId(window.location.href); const lastWatched = getLastWatchedTime(courseId); if (lastWatched > 0) { video.currentTime = lastWatched; console.log(`从 ${lastWatched} 秒开始播放`); } video.play().then(() => { console.log('视频开始播放'); currentVideo = video; const checkProgress = setInterval(() => { if (!isPlaying) { clearInterval(checkProgress); return; } const currentTime = video.currentTime; const duration = video.duration; if (duration && !isNaN(duration)) { const percent = Math.floor((currentTime / duration) * 100); updateProgress(percent); if (currentTime - getLastWatchedTime(courseId) > 5) { saveProgress(courseId, currentTime); } if (currentTime >= duration - 2) { clearInterval(checkProgress); console.log('视频播放完成'); saveProgress(courseId, duration); resolve(true); } } }, POLL_INTERVAL); }).catch(err => { console.error('播放失败:', err); reject(err); }); } else if (video.tagName === 'IFRAME') { console.log('视频在iframe中,尝试等待...'); updateStatus('视频在iframe中,请在iframe内手动播放'); setTimeout(() => resolve(true), 30000); } } catch (error) { console.error('播放出错:', error); reject(error); } }); } function navigateTo(url) { return new Promise(resolve => { window.location.href = url; const checkUrl = setInterval(() => { if (window.location.href === url || window.location.href.includes(url)) { clearInterval(checkUrl); setTimeout(resolve, 3000); } }, 1000); }); } async function startAutoPlay() { if (isPlaying) { showNotification('已经在运行中', 'info'); return; } courseList = findCourseLinks(); if (courseList.length === 0) { showNotification('未找到课程链接,请确保在课程列表页面', 'error'); return; } updateCourseList(courseList.length); isPlaying = true; updateStatus('自动播放中...'); showNotification(`开始自动播放 ${courseList.length} 个视频`, 'success'); try { for (let i = 0; i < courseList.length && isPlaying; i++) { currentCourseIndex = i; const course = courseList[i]; updateStatus(`正在播放第 ${i + 1}/${courseList.length} 个视频`); updateCurrentCourse(course.title); console.log(`正在打开: ${course.title} (${course.href})`); await navigateTo(course.href); const video = findVideo(); if (video) { try { await playVideo(video); } catch (err) { console.error(`视频 ${i + 1} 播放失败:`, err); showNotification(`视频 ${i + 1} 播放失败: ${err.message}`, 'error'); } } else { showNotification('未找到视频元素', 'error'); await new Promise(resolve => setTimeout(resolve, 5000)); } if (i < courseList.length - 1 && isPlaying) { console.log('返回课程列表...'); window.history.back(); await new Promise(resolve => setTimeout(resolve, 3000)); } } if (isPlaying) { showNotification('所有视频播放完成!', 'success'); updateStatus('播放完成'); } } catch (error) { console.error('自动播放出错:', error); showNotification('播放出错: ' + error.message, 'error'); } finally { isPlaying = false; } } function stopAutoPlay() { isPlaying = false; updateStatus('已停止'); if (currentVideo) { currentVideo.pause(); } showNotification('已停止自动播放', 'info'); } function resetProgress() { if (confirm('确定要重置所有观看进度吗?')) { GM_setValue(STORAGE_KEY, '{}'); showNotification('进度已重置', 'success'); updateProgress(0); } } function init() { if (document.getElementById('yiboshi-auto-panel')) { return; } addControlPanel(); updateStatus('准备就绪'); console.log('医博士自动看课脚本已加载'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();