// ==UserScript==
// @name 双卫网自动看课 (增强版)
// @namespace http://tampermonkey.net/
// @version 1.2
// @description 双卫网自动连续播放课程视频,支持课程列表自动遍历
// @author You
// @match https://www.sww.com.cn/*
// @match https://www.shuangwei.cn/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @run-at document-idle
// @noframes
// ==/UserScript==
(function() {
'use strict';
const STORAGE_KEY = 'shuangwei_auto_play_progress';
const POLL_INTERVAL = 15000;
let isPlaying = false;
let currentVideo = null;
let courseList = [];
let currentCourseIndex = 0;
let autoPlayTimer = null;
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 = 'shuangwei-auto-panel';
panel.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 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('shuangwei-start').addEventListener('click', startAutoPlay);
document.getElementById('shuangwei-stop').addEventListener('click', stopAutoPlay);
document.getElementById('shuangwei-reset').addEventListener('click', resetProgress);
GM_addStyle(`
#shuangwei-start:hover { background: #45a049 !important; transform: scale(1.02); }
#shuangwei-stop:hover { background: #da190b !important; transform: scale(1.02); }
#shuangwei-reset:hover { background: #e68900 !important; }
`);
}
function updateStatus(status) {
const statusElem = document.getElementById('shuangwei-status');
if (statusElem) {
statusElem.textContent = `状态: ${status}`;
}
}
function updateCurrentCourse(courseName) {
const courseElem = document.getElementById('shuangwei-current');
if (courseElem) {
courseElem.textContent = `当前课程: ${courseName}`;
}
}
function updateProgress(percent) {
const progressElem = document.getElementById('shuangwei-progress');
if (progressElem) {
progressElem.textContent = `观看进度: ${percent}%`;
}
}
function updateCourseList(count) {
const coursesElem = document.getElementById('shuangwei-courses');
if (coursesElem) {
coursesElem.textContent = `找到 ${count} 个视频`;
}
}
function findCourseLinks() {
const links = [];
const courseItems = document.querySelectorAll('.course-list .course-item, [class*="course"], [class*="lesson"], tr[class*="course"], tr[class*="lesson"]');
courseItems.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) {
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;
}
}
return null;
}
function getCourseId(url) {
const match = url.match(/\/(\d+)$/) || url.match(/id=(\d+)/) || url.match(/\/(\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('shuangwei-auto-panel')) {
return;
}
addControlPanel();
updateStatus('准备就绪');
console.log('双卫网自动看课脚本已加载');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();