// ==UserScript== // @name 北医继教自动点击按钮 // @namespace http://tampermonkey.net/ // @version 0.3.1 // @description 自动跳过暂停和弹窗,播放进度达到100%时自动切换到下一个视频 // @author Mzky // @match https://bjmu-kfkc.webtrn.cn/learnspace/learn/learn/templateeight/courseware_index.action?* // @grant none // ==/UserScript== (function() { 'use strict'; // 设置检查间隔时间(毫秒) const CHECK_INTERVAL = 3000; // 每10秒检查一次 const ACTION_DELAY = 1000; // 操作延迟时间(毫秒) // 定义检查函数 function checkAndClickButton() { try { // 获取视频元素 - 使用增强的查找策略 var video = findVideoElement(); // 如果找到了视频元素 if (video) { console.log("找到视频元素"); // 检查视频是否播放完成 if (isVideoComplete(video)) { console.log("视频播放完成,切换到下一个视频"); switchToNextVideo(); } else if (video.paused) { video.play(); console.log("视频已恢复播放"); } } else { console.log("未找到视频元素"); // 作为备选方案,尝试检查播放器暂停按钮 checkPlayerPauseButton(); } } catch(e) { console.log("video_catch:", e); } try { var confirmBtn = document.querySelector('.layui-layer-dialog .layui-layer-btn .layui-layer-btn0'); if (confirmBtn) { confirmBtn.click(); console.log("已成功模拟点击'确定'按钮"); } else { console.log("未找到确定按钮"); } } catch (e) { console.log("confirmBtn_catch:", e); } } // 增强的视频元素查找方法 function findVideoElement() { // 查找策略数组,按优先级排序 const findStrategies = [ // 1. 尝试在当前文档中查找 #container video () => document.querySelector("#container video"), // 2. 尝试直接查找任何 video 元素 () => document.querySelector("video"), // 3. 尝试查找常见的视频容器内的 video 元素 () => document.querySelector(".video-container video"), () => document.querySelector(".player video"), () => document.querySelector(".video-wrapper video"), // 4. 尝试在 iframe 中查找 () => { const iframes = document.querySelectorAll('iframe'); for (let iframe of iframes) { try { const video = iframe.contentDocument?.querySelector('video'); if (video) return video; } catch (e) { // 忽略跨域错误 } } return null; }, // 5. 尝试查找所有可能的视频元素 () => { const videos = document.querySelectorAll('video'); return videos.length > 0 ? videos[0] : null; } ]; // 尝试所有策略 for (let i = 0; i < findStrategies.length; i++) { try { const video = findStrategies[i](); if (video) { console.log(`使用查找策略 #${i+1} 找到视频元素`); return video; } } catch (e) { console.log(`查找策略 #${i+1} 出错:`, e); } } // 如果在当前文档中找不到,尝试在父文档中查找 if (window.parent && window.parent.document) { try { const parentFindStrategies = [ () => window.parent.document.querySelector("#container video"), () => window.parent.document.querySelector("video"), () => window.parent.document.querySelector(".video-container video"), () => window.parent.document.querySelector(".player video"), () => { const videos = window.parent.document.querySelectorAll('video'); return videos.length > 0 ? videos[0] : null; } ]; for (let i = 0; i < parentFindStrategies.length; i++) { try { const video = parentFindStrategies[i](); if (video) { console.log(`在父文档中使用查找策略 #${i+1} 找到视频元素`); return video; } } catch (e) { console.log(`父文档查找策略 #${i+1} 出错:`, e); } } } catch (e) { console.log("无法访问父文档: ", e); } } // 记录页面上所有可能的视频相关信息用于调试 logVideoDebugInfo(); return null; } // 记录视频相关的调试信息 function logVideoDebugInfo() { try { // 记录页面中所有的视频元素 const allVideos = document.querySelectorAll('video'); console.log(`页面中共有 ${allVideos.length} 个视频元素`); allVideos.forEach((video, index) => { console.log(`视频元素 ${index}:`, video); console.log(` ID: ${video.id}`); console.log(` Class: ${video.className}`); console.log(` 父元素: ${video.parentElement?.id || video.parentElement?.className || '未知'}`); }); // 记录可能的容器元素 const possibleContainers = document.querySelectorAll('#container, .video-container, .player, .video-wrapper'); console.log(`找到 ${possibleContainers.length} 个可能的视频容器`); possibleContainers.forEach(container => { console.log(`容器: ${container.id || container.className}`, container); }); } catch (e) { console.log("记录调试信息时出错: ", e); } } // 备选方法:检查播放器暂停按钮 function checkPlayerPauseButton() { try { // 尝试多种暂停按钮选择器 const pauseButtonSelectors = [ "#player_pause", ".player-pause", ".pause-button", "[class*='pause']", "[id*='pause']" ]; for (let selector of pauseButtonSelectors) { const playerPauseBtn = document.querySelector(selector); if (playerPauseBtn && getComputedStyle(playerPauseBtn).display !== 'none') { playerPauseBtn.click(); console.log(`已点击暂停按钮 (${selector}),恢复视频播放`); return; } } console.log("未找到可见的暂停按钮"); } catch (e) { console.log("playerPauseBtn_catch:", e); } } // 判断视频是否播放完成(100%) function isVideoComplete(video) { // 检查视频是否存在 if (!video) { console.log("视频元素不存在"); return false; } // 检查视频是否有有效时长 if (!video.duration) { console.log("视频时长无效"); return false; } // 检查当前时间是否与时长差小于0.1秒(认为是播放完成) return Math.abs(video.currentTime - video.duration) < 0.1; } // 切换到下一个视频 - 基于courseware_index.action文件结构优化 function switchToNextVideo() { try { // 1. 获取页面中存储的相关信息 const prevItemId = document.getElementById('prev_item_id')?.value || ''; const currentCourseId = document.getElementById('currentCourseId')?.value || ''; const showSectionId = document.getElementById('show_section_id')?.value || '1'; console.log(`当前课程ID: ${currentCourseId}, 上一节点ID: ${prevItemId}, 显示章节ID: ${showSectionId}`); // 2. 展开所有章节以确保能找到所有视频 expandAllSections(); // 3. 获取所有视频项,确保只选择itemType为video且存在onclick属性的元素 const allVideoItems = Array.from(document.querySelectorAll('.s_point[itemtype="video"][onclick*="openLearnResItem"]')); if (allVideoItems.length === 0) { console.log("未找到可播放的视频项"); return; } console.log(`共找到 ${allVideoItems.length} 个可播放的视频项`); // 4. 找到当前视频项 let currentVideoItem = null; // 首先尝试根据prev_item_id找到当前视频 if (prevItemId) { currentVideoItem = document.getElementById(prevItemId); } // 如果找不到,尝试根据s_pointerct类名查找 if (!currentVideoItem) { currentVideoItem = allVideoItems.find(item => item.classList.contains('s_pointerct') ); } // 如果还是找不到,尝试查找最近完成的视频(completestate=1) if (!currentVideoItem) { const completedVideos = allVideoItems.filter(item => item.getAttribute('completestate') === '1' ); if (completedVideos.length > 0) { currentVideoItem = completedVideos[completedVideos.length - 1]; } } // 5. 获取当前视频索引 const currentIndex = currentVideoItem ? allVideoItems.findIndex(item => item.id === currentVideoItem.id) : -1; console.log(`当前视频索引: ${currentIndex}, 当前视频ID: ${currentVideoItem?.id || '未找到'}`); // 6. 查找下一个未完成的视频 let nextVideoItem = null; // 优先查找当前视频之后的未完成视频 if (currentIndex >= 0) { nextVideoItem = allVideoItems.slice(currentIndex + 1).find(item => item.getAttribute('completestate') === '0' || item.getAttribute('completestate') === null ); } // 如果当前视频之后没有未完成的视频,查找所有未完成的视频 if (!nextVideoItem) { nextVideoItem = allVideoItems.find(item => item.getAttribute('completestate') === '0' || item.getAttribute('completestate') === null ); } // 如果没有未完成的视频,选择当前视频的下一个视频 if (!nextVideoItem && currentIndex >= 0 && currentIndex < allVideoItems.length - 1) { nextVideoItem = allVideoItems[currentIndex + 1]; } // 7. 如果找到了下一个视频,执行切换操作 if (nextVideoItem) { const videoTitle = nextVideoItem.getAttribute('title') || nextVideoItem.querySelector('.s_pointti')?.textContent || '未知标题'; const videoId = nextVideoItem.id; const completeState = nextVideoItem.getAttribute('completestate'); console.log(`找到下一个视频: [${videoId}] ${videoTitle} (完成状态: ${completeState})`); // 8. 确保视频所在的章节已展开 ensureSectionVisible(nextVideoItem); // 9. 模拟点击下一个视频,使用setTimeout确保章节展开完成 setTimeout(() => { console.log(`正在切换到视频: ${videoTitle}`); // 尝试使用页面提供的openLearnResItem函数(如果存在) if (window.openLearnResItem) { try { // 从onclick属性中提取参数 const onclickAttr = nextVideoItem.getAttribute('onclick'); const match = onclickAttr?.match(/openLearnResItem\('([^']+)',\s*'([^']+)',\s*([^,]+),\s*'([^']+)'\)/); if (match) { const [, itemId, itemType, params, title] = match; window.openLearnResItem(itemId, itemType, params, title); console.log(`使用openLearnResItem函数切换视频成功`); return; } } catch (e) { console.log(`调用openLearnResItem函数失败:`, e); } } // 如果openLearnResItem函数不存在或调用失败,使用click方法 nextVideoItem.click(); console.log(`已点击视频元素切换成功`); // 更新prev_item_id为当前视频ID const prevItemInput = document.getElementById('prev_item_id'); if (prevItemInput) { prevItemInput.value = videoId; console.log(`已更新prev_item_id为: ${videoId}`); } }, ACTION_DELAY * 2); // 增加延迟时间确保章节完全展开 } else { console.log("未找到下一个可播放的视频项"); } } catch (e) { console.log("切换视频出错:", e); console.log("错误堆栈:", e.stack); } } // 展开所有章节 function expandAllSections() { try { // 展开所有章 const chapters = document.querySelectorAll('.s_chapter'); chapters.forEach(chapter => { // 检查章节是否已展开 const nextElement = chapter.nextElementSibling; if (nextElement && nextElement.classList.contains('s_sectionlist') && nextElement.style.display === 'none') { chapter.click(); } }); console.log(`已尝试展开所有章,共${chapters.length}个章`); // 展开所有讲 const sections = document.querySelectorAll('.s_section'); sections.forEach(section => { // 检查讲是否已展开 const nextElement = section.nextElementSibling; if (nextElement && nextElement.classList.contains('s_sectionwrap') && nextElement.style.display === 'none') { section.click(); } }); console.log(`已尝试展开所有讲,共${sections.length}个讲`); } catch (e) { console.log("展开章节时出错:", e); } } // 确保视频所在的章节可见 function ensureSectionVisible(videoItem) { try { // 找到视频所在的讲包裹元素 const sectionWrap = videoItem.closest('.s_sectionwrap'); if (!sectionWrap) { console.log("未找到视频所在的讲包裹元素"); return; } // 检查讲是否可见,如果不可见则点击讲标题展开 if (sectionWrap.style.display === 'none') { const sectionTitle = sectionWrap.previousElementSibling; if (sectionTitle && sectionTitle.classList.contains('s_section')) { sectionTitle.click(); console.log(`已展开讲: ${sectionTitle.textContent.trim()}`); } } // 找到讲所在的章包裹元素 const chapterList = sectionWrap.closest('.s_sectionlist'); if (!chapterList) { console.log("未找到讲所在的章包裹元素"); return; } // 检查章是否可见,如果不可见则点击章标题展开 if (chapterList.style.display === 'none') { const chapterTitle = chapterList.previousElementSibling; if (chapterTitle && chapterTitle.classList.contains('s_chapter')) { chapterTitle.click(); console.log(`已展开章: ${chapterTitle.textContent.trim()}`); } } } catch (e) { console.log("确保章节可见时出错:", e); } } // 开始循环检查 var intervalId = setInterval(checkAndClickButton, CHECK_INTERVAL); // 页面关闭时清理定时器 window.addEventListener('beforeunload', () => { clearInterval(intervalId); }); })();