videoPlayer

岐黄天使刷课助手的视频播放控制模块,负责视频的自动播放、暂停和状态管理。

Dette scriptet burde ikke installeres direkte. Det er et bibliotek for andre script å inkludere med det nye metadirektivet // @require https://update.greasyfork.org/scripts/537083/1597542/videoPlayer.js

// ==UserScript==
// @name         岐黄天使刷课助手 - 视频播放模块
// @namespace    http://tampermonkey.net/qhtx-modules
// @version      1.3.2
// @description  岐黄天使刷课助手的视频播放控制模块,负责视频的自动播放、暂停和状态管理。
// @author       AI助手
// ==/UserScript==

// 视频播放模块
(function() {
    'use strict';

    // 切换自动学习状态
    window.toggleAutoLearn = function() {
        // 获取当前状态,如果GM_getValue不可用,则使用localStorage
        let isRunning;
        if (typeof GM_getValue !== 'undefined') {
            isRunning = GM_getValue('qh-is-running', false);
        } else {
            isRunning = localStorage.getItem('qh-is-running') === 'true';
        }

        // 切换状态
        isRunning = !isRunning;

        // 保存新状态
        if (typeof GM_setValue !== 'undefined') {
            GM_setValue('qh-is-running', isRunning);
        } else {
            localStorage.setItem('qh-is-running', isRunning.toString());
        }

        console.log('切换自动学习状态:', isRunning ? '开始' : '暂停');

        if (isRunning) {
            // 开始自动学习
            if(window.qh && window.qh.updateStatus) window.qh.updateStatus('自动学习已开始');

            // 设置定时器,定期检查和播放视频
            if (!window.qh.autoPlayInterval) {
                window.qh.autoPlayInterval = setInterval(autoPlayVideo, 5000);

                // 立即执行一次
                setTimeout(autoPlayVideo, 500);
            }

            // 尝试播放所有视频
            setTimeout(() => {
                try {
                    // 直接调用视频播放函数
                    const videos = document.querySelectorAll('video');
                    if (videos.length > 0) {
                        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('找到视频,尝试播放');
                        console.log('找到视频,尝试播放');

                        videos.forEach(video => {
                            // 设置视频属性
                            video.loop = true;
                            video.muted = true;
                            video.playbackRate = 2.0;

                            // 尝试播放视频
                            try {
                                const playPromise = video.play();

                                if (playPromise !== undefined) {
                                    playPromise.then(() => {
                                        console.log('视频播放成功');
                                        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('视频播放成功');
                                    }).catch(error => {
                                        console.error('视频播放失败:', error);
                                        // 尝试使用其他方法播放
                                        setTimeout(() => {
                                            try {
                                                video.play();
                                            } catch (e) {
                                                console.error('重试播放失败:', e);
                                            }
                                        }, 1000);
                                    });
                                }
                            } catch (e) {
                                console.error('播放视频出错:', e);
                            }
                        });
                    }

                    // 检查iframe中的视频
                    const frames = document.querySelectorAll('iframe');
                    for (const frame of frames) {
                        try {
                            const frameDoc = frame.contentDocument || frame.contentWindow.document;
                            const frameVideos = frameDoc.querySelectorAll('video');

                            if (frameVideos.length > 0) {
                                if(window.qh && window.qh.updateStatus) window.qh.updateStatus('在iframe中找到视频,尝试播放');
                                console.log('在iframe中找到视频,尝试播放');

                                frameVideos.forEach(video => {
                                    // 设置视频属性
                                    video.loop = false; // 不循环播放
                                    video.muted = true;
                                    // 不修改播放速度,使用默认速度

                                    // 尝试播放视频
                                    try {
                                        const playPromise = video.play();

                                        if (playPromise !== undefined) {
                                            playPromise.then(() => {
                                                console.log('iframe视频播放成功');
                                                if(window.qh && window.qh.updateStatus) window.qh.updateStatus('iframe视频播放成功');
                                            }).catch(error => {
                                                console.error('iframe视频播放失败:', error);
                                                // 尝试使用其他方法播放
                                                setTimeout(() => {
                                                    try {
                                                        video.play();
                                                    } catch (e) {
                                                        console.error('重试播放iframe视频失败:', e);
                                                    }
                                                }, 1000);
                                            });
                                        }
                                    } catch (e) {
                                        console.error('播放iframe视频出错:', e);
                                    }
                                });
                            }
                        } catch (e) {
                            console.error('无法访问iframe内容:', e);
                        }
                    }
                } catch (e) {
                    console.error('播放视频出错:', e);
                }
            }, 1000);
        } else {
            // 暂停自动学习
            if(window.qh && window.qh.updateStatus) window.qh.updateStatus('自动学习已暂停');

            // 清除定时器
            if (window.qh.autoPlayInterval) {
                clearInterval(window.qh.autoPlayInterval);
                window.qh.autoPlayInterval = null;
            }

            // 暂停所有视频
            pauseAllVideos();
        }

        // 更新按钮状态
        if(window.qh && window.qh.updateButtonStatus) window.qh.updateButtonStatus();
    };

    // 播放iframe中的视频
    window.playAllVideos = function() {
        // 尝试播放iframe中的视频
        const frames = document.querySelectorAll('iframe');
        frames.forEach(frame => {
            try {
                const frameDoc = frame.contentDocument || frame.contentWindow.document;
                const frameVideos = frameDoc.querySelectorAll('video');
                frameVideos.forEach(video => {
                    // 设置视频属性
                    video.loop = false; // 不循环播放
                    video.muted = true;
                    // 不修改播放速度,使用默认速度

                    // 尝试播放视频
                    try {
                        const playPromise = video.play();

                        // 处理播放承诺
                        if (playPromise !== undefined) {
                            playPromise.then(() => {
                                console.log('iframe视频播放成功');
                            }).catch(error => {
                                console.error('iframe视频播放失败:', error);
                                // 尝试使用其他方法播放
                                setTimeout(() => {
                                    try {
                                        video.play();
                                    } catch (e) {
                                        console.error('重试播放iframe视频失败:', e);
                                    }
                                }, 1000);
                            });
                        }
                    } catch (e) {
                        console.error('播放iframe视频出错:', e);
                    }
                });
            } catch (e) {
                console.error('无法访问iframe内容:', e);
            }
        });
    };

    // 暂停所有视频
    window.pauseAllVideos = function() {
        document.querySelectorAll('video').forEach(video => video.pause());
        const frames = document.querySelectorAll('iframe');
        frames.forEach(frame => {
            try {
                const frameDoc = frame.contentDocument || frame.contentWindow.document;
                frameDoc.querySelectorAll('video').forEach(video => video.pause());
            } catch (e) { /*忽略*/ }
                });
        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('所有视频已暂停');
        console.log('所有视频已暂停');
    };

    // 主要功能:自动播放视频
    window.autoPlayVideo = function() {
        console.log('[自动播放] 检查视频...');
        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('自动播放检查中...');

        let videoPlayed = false;

        // 改进视频检测逻辑,不依赖标记属性
        const allVideos = [];

        // 检查主文档中的视频
        const mainVideos = Array.from(document.querySelectorAll('video'));
        allVideos.push(...mainVideos);

        // 检查iframe中的视频(增强错误处理)
        const frames = document.querySelectorAll('iframe');
        for (const frame of frames) {
            try {
                // 尝试多种方式访问iframe内容
                let frameDoc = null;
                try {
                    frameDoc = frame.contentDocument;
                } catch (e) {
                    try {
                        frameDoc = frame.contentWindow.document;
                    } catch (e2) {
                        console.debug('[自动播放] 无法访问iframe内容 (跨域限制):', frame.src);
                        continue;
                    }
                }

                if (frameDoc) {
                    const frameVideos = Array.from(frameDoc.querySelectorAll('video'));
                    allVideos.push(...frameVideos);
                    console.log(`[自动播放] 在iframe中找到 ${frameVideos.length} 个视频`);
                }
            } catch (e) {
                console.debug('[自动播放] 跳过无法访问的iframe:', e.message);
            }
        }

        if (allVideos.length === 0) {
            if(window.qh && window.qh.updateStatus) window.qh.updateStatus('未找到可播放视频');
            console.log('[自动播放] 未找到视频元素。');

            // 如果没有视频,尝试导航到下一课
            if (window.qh && window.qh.courseList && window.qh.courseList.length > 0) {
                console.log('[自动播放] 尝试导航到列表中的下一课。');
                if(window.qh && window.qh.updateStatus) window.qh.updateStatus('尝试导航下一课');
                if (typeof navigateToNextCourse === 'function') {
                    navigateToNextCourse();
                } else if (typeof window.navigateToNextCourse === 'function') {
                    window.navigateToNextCourse();
                }
            }
            return;
        }

        console.log(`[自动播放] 找到 ${allVideos.length} 个视频元素`);

        // 改进视频播放逻辑
        for (const video of allVideos) {
            // 检查视频是否需要播放(更全面的条件)
            const needsPlay = video.paused ||
                             video.ended ||
                             video.readyState === 0 ||
                             (video.currentTime === 0 && video.duration > 0);

            if (needsPlay) {
                console.log('[自动播放] 找到需要播放的视频,尝试播放:', {
                    paused: video.paused,
                    ended: video.ended,
                    readyState: video.readyState,
                    currentTime: video.currentTime,
                    duration: video.duration
                });

                // 设置视频属性
                video.loop = false; // 确保非循环,以便ended事件触发
                video.muted = true;

                // 尝试播放视频
                const playPromise = video.play();
                if (playPromise !== undefined) {
                    playPromise.then(() => {
                        console.log('[自动播放] 视频开始播放成功');
                        updateVideoListeners(video); // 添加或更新事件监听器
                        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('视频播放中...');

                        // 尝试检测当前课程名称
                        const courseName = detectCurrentCourseInHierarchy(video) || '视频课程';
                        if(window.qh && window.qh.updateCurrentCourse) window.qh.updateCurrentCourse(courseName);

                        videoPlayed = true;
                    }).catch(error => {
                        console.warn('[自动播放] 播放视频失败:', error.message);
                        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('视频播放失败: ' + error.message);
                    });
                } else {
                    console.log('[自动播放] 视频播放方法未返回Promise,可能已开始播放');
                    videoPlayed = true;
                }

                if (videoPlayed) break; // 每次只尝试播放一个视频
            }
        }

        if (!videoPlayed) {
            console.log('[自动播放] 所有视频都在播放中或无需操作。');
            if(window.qh && window.qh.updateStatus) window.qh.updateStatus('视频状态正常');
        }
    };

    // 检测当前课程名称的辅助函数
    function detectCurrentCourseInHierarchy(videoElement) {
        try {
            // 尝试从页面标题获取
            const titleElement = document.getElementById('kcTitle');
            if (titleElement && titleElement.textContent.trim()) {
                return titleElement.textContent.trim();
            }

            // 尝试从视频元素的父级容器获取
            if (videoElement) {
                let parent = videoElement.parentElement;
                while (parent && parent !== document.body) {
                    const titleEl = parent.querySelector('.title, .course-title, .video-title, h1, h2, h3');
                    if (titleEl && titleEl.textContent.trim()) {
                        return titleEl.textContent.trim();
                    }
                    parent = parent.parentElement;
                }
            }

            // 尝试从当前课程列表获取
            if (window.qh && window.qh.courseList && window.qh.currentCourseIndex >= 0) {
                const currentCourse = window.qh.courseList[window.qh.currentCourseIndex];
                if (currentCourse && currentCourse.title) {
                    return currentCourse.title;
                }
            }

            return null;
        } catch (e) {
            console.debug('检测课程名称时出错:', e);
            return null;
        }
    }

    // 查找并点击未完成的课程
    window.findAndClickUnfinishedCourse = function(doc) {
        // 查找未完成的视频链接
        const unfinishedLinks = doc.querySelectorAll('.append-plugin-tip > a, .content-unstart a, .content-learning a');
        if (unfinishedLinks.length > 0) {
            updateStatus('找到未完成的课程,即将播放');

            // 收集所有课程链接,用于上一课/下一课导航
            if (typeof collectCourseLinks === 'function') {
                collectCourseLinks(doc);
            }

            // 点击第一个未完成的课程
            try {
                unfinishedLinks[0].click();
            } catch (e) {
                console.error('点击未完成课程失败:', e);
                updateStatus('点击未完成课程失败');
            }
        } else {
            updateStatus('未找到未完成的课程,可能已全部完成');

            // 检查是否有课程列表
            const coursesInList = doc.querySelectorAll('.mycourse-row');
            if (coursesInList.length > 0) {
                // 收集课程列表
                if (typeof collectCoursesFromList === 'function') {
                    collectCoursesFromList(doc);
                }

                // 遍历课程列表,查找未完成的课程
                for (let i = 0; i < coursesInList.length; i++) {
                    const courseRow = coursesInList[i];
                    const courseState = courseRow.innerText.includes('未完成') || courseRow.innerText.includes('未开始');

                    if (courseState) {
                        updateStatus('在课程列表中找到未完成课程,即将打开');

                        try {
                            const courseLink = courseRow.querySelector('a');
                            if (courseLink) {
                                courseLink.click();
                                break;
                            }
                        } catch (e) {
                            console.error('点击课程列表中的未完成课程失败:', e);
                        }
                    }
                }
            }
        }
    };

    // 检查进度指示器
    window.checkProgressIndicator = function(doc) {
        // 首先尝试从<div class="jc_hd w-80">当前视频已观看时长:<span id="schedule">100%</span>获取进度
        const scheduleElement = doc.getElementById('schedule');
        if (scheduleElement) {
            const progressText = scheduleElement.textContent.trim();
            const progressMatch = progressText.match(/(\d+)%/);
            if (progressMatch && progressMatch[1]) {
                const progress = parseInt(progressMatch[1]);
                console.log('从schedule元素获取到进度:', progress + '%');
                updateProgress(progress);

                // 如果进度为100%,自动切换到下一个课程
                if (progress === 100) {
                    // 防止重复触发
                    if (window.qh.isNavigating) {
                        console.log('导航已在进行中,忽略进度100%事件');
                        return;
                    }

                    console.log('当前视频已完成,准备切换到下一个课程');
                    updateStatus('视频播放完成,准备切换到下一个课程');

                    // 设置导航状态标志
                    window.qh.isNavigating = true;

                    // 延迟一秒后切换到下一课
                    setTimeout(() => {
                        // 使用navigateToNextCourse函数切换到下一课
                        if (typeof navigateToNextCourse === 'function') {
                            navigateToNextCourse();
                        }

                        // 5秒后重置导航状态标志,避免卡死
                        setTimeout(() => {
                            window.qh.isNavigating = false;
                        }, 5000);
                    }, 1000);
                }
                return;
            }
        }

        // 如果没有找到schedule元素,尝试其他进度指示器
        const progressIndicator = doc.getElementById('realPlayVideoTime');
        if (progressIndicator) {
            const progress = parseInt(progressIndicator.innerText);
            updateProgress(progress);

            // 如果进度为100%,自动切换到下一个课程
            if (progress === 100) {
                // 防止重复触发
                if (window.qh.isNavigating) {
                    console.log('导航已在进行中,忽略进度100%事件');
                    return;
                }

                console.log('当前视频已完成,准备切换到下一个课程');
                updateStatus('视频播放完成,准备切换到下一个课程');

                // 设置导航状态标志
                window.qh.isNavigating = true;

                // 延迟一秒后切换到下一课
                setTimeout(() => {
                    // 使用navigateToNextCourse函数切换到下一课
                    if (typeof navigateToNextCourse === 'function') {
                        navigateToNextCourse();
                    }

                    // 5秒后重置导航状态标志,避免卡死
                    setTimeout(() => {
                        window.qh.isNavigating = false;
                    }, 5000);
                }, 1000);
            }
        }

        // 尝试查找其他可能的进度指示器
        const jcHdElements = doc.querySelectorAll('.jc_hd');
        for (const jcHd of jcHdElements) {
            if (jcHd.textContent.includes('当前视频已观看时长')) {
                const progressText = jcHd.textContent;
                const progressMatch = progressText.match(/(\d+)%/);
                if (progressMatch && progressMatch[1]) {
                    const progress = parseInt(progressMatch[1]);
                    console.log('从jc_hd元素获取到进度:', progress + '%');
                    updateProgress(progress);

                    // 如果进度为100%,自动切换到下一个课程
                    if (progress === 100) {
                        // 防止重复触发
                        if (window.qh.isNavigating) {
                            console.log('导航已在进行中,忽略进度100%事件');
                            return;
                        }

                        console.log('当前视频已完成,准备切换到下一个课程');
                        updateStatus('视频播放完成,准备切换到下一个课程');

                        // 设置导航状态标志
                        window.qh.isNavigating = true;

                        // 延迟一秒后切换到下一课
                        setTimeout(() => {
                            // 使用navigateToNextCourse函数切换到下一课
                            if (typeof navigateToNextCourse === 'function') {
                                navigateToNextCourse();
                            }

                            // 5秒后重置导航状态标志,避免卡死
                            setTimeout(() => {
                                window.qh.isNavigating = false;
                            }, 5000);
                        }, 1000);
                    }
                    return;
                }
            }
        }
    };

    function updateVideoListeners(videoElement) {
        if (!videoElement) return;
        const qh = window.qh || {};

        const onPlay = () => { if(qh.updateStatus) qh.updateStatus('视频播放中...'); };
        const onPause = () => { if(qh.updateStatus) qh.updateStatus('视频已暂停'); };
        const onEnded = () => handleVideoEnd(videoElement); // 直接传递videoElement
        const onTimeUpdate = () => {
            if (videoElement.duration && videoElement.currentTime) {
                const progress = (videoElement.currentTime / videoElement.duration) * 100;
                if(qh.updateProgress) qh.updateProgress(Math.round(progress));
            }
        };

        videoElement.addEventListener('play', onPlay);
        videoElement.addEventListener('pause', onPause);
        videoElement.addEventListener('ended', onEnded);
        videoElement.addEventListener('timeupdate', onTimeUpdate);

        // 存储事件监听器以便移除
        videoElement.qhListeners = { onPlay, onPause, onEnded, onTimeUpdate };
        if(qh.updateStatus) qh.updateStatus('视频监听器已更新');
    }
    window.updateVideoListeners = updateVideoListeners;

    function handleVideoEnd(videoElement) {
        console.log('视频播放结束事件触发');
        if(window.qh && window.qh.updateStatus) window.qh.updateStatus('视频播放完毕');
        if(window.qh && window.qh.updateProgress) window.qh.updateProgress(100);

        if (window.qh && window.qh.dailyLimitManager) {
            window.qh.dailyLimitManager.notifyVideoWatched();
            if (window.qh.dailyLimitManager.state.isLimitReached) {
                console.log('[VideoPlayer] 每日上限达到,停止自动播放。');
                if(window.qh && window.qh.updateStatus) window.qh.updateStatus('每日上限,停止播放');
                // 如果 toggleAutoLearn 是全局的,并且当前是运行状态,则调用它来暂停
                let isRunning = false;
                if (typeof GM_getValue !== 'undefined') { isRunning = GM_getValue('qh-is-running', false); }
                else { isRunning = localStorage.getItem('qh-is-running') === 'true'; }
                if (isRunning && typeof window.toggleAutoLearn === 'function') {
                    window.toggleAutoLearn(); // 这会设置 isRunning 为 false 并更新UI
                }
                return; // 不再尝试播放下一个
            }
        }

        // 尝试播放列表中的下一个视频或课程
        if (typeof navigateToNextCourse === 'function') {
            console.log('准备导航到下一课...');
            if(window.qh && window.qh.updateStatus) window.qh.updateStatus('准备导航到下一课...');
            setTimeout(navigateToNextCourse, 2000); // 延迟2秒再跳转
        } else {
            console.warn('navigateToNextCourse 函数不可用');
             if(window.qh && window.qh.updateStatus) window.qh.updateStatus('无法自动导航到下一课');
        }
    }
    window.handleVideoEnd = handleVideoEnd;

    function detectCurrentCourseInHierarchy(videoElement) {
        let currentElement = videoElement;
        for (let i = 0; i < 5; i++) { // 向上查找5层
            if (!currentElement) break;
            // 尝试常见的标题类名或ID (需要根据实际网站结构调整)
            const titleEl = currentElement.querySelector('.title, .course-title, #courseName, #videoName');
            if (titleEl && titleEl.textContent.trim()) {
                return titleEl.textContent.trim();
            }
            currentElement = currentElement.parentElement;
        }
        return null;
    }

    console.log('[模块加载] videoPlayer 模块已加载');

    // 模块加载时检查并恢复自动播放状态
    (function() {
        const isRunning = GM_getValue('qh-is-running', false);
        console.log('[VideoPlayer] 模块加载时,检测到 qh-is-running:', isRunning);
        if (isRunning) {
            console.log('[VideoPlayer] 自动学习先前已开启,尝试恢复播放和UI状态。');
            // 确保UI状态更新
            if (window.qh && typeof window.qh.updateStatus === 'function') {
                window.qh.updateStatus('自动学习进行中...');
            }
            if (window.qh && typeof window.qh.updateButtonStatus === 'function') {
                window.qh.updateButtonStatus(); // 这会更新按钮文本为"暂停"
            }

            // 启动或恢复播放检查定时器
            if (!window.qh.autoPlayInterval) {
                console.log('[VideoPlayer] 恢复 autoPlayInterval 定时器。');
                window.qh.autoPlayInterval = setInterval(autoPlayVideo, 5000); // 与 toggleAutoLearn 中一致
                setTimeout(autoPlayVideo, 500); // 立即尝试播放
            } else {
                console.log('[VideoPlayer] autoPlayInterval 定时器已存在。');
            }
            // 确保视频尝试播放的逻辑也执行
             setTimeout(() => autoPlayVideo(), 1000); // 延迟一点确保页面元素加载完毕
        } else {
            console.log('[VideoPlayer] 自动学习先前未开启或已手动暂停。');
        }
    })();
})();