Greasy Fork is available in English.

国家开放大学课程视频后台播放

提升学习效率

// ==UserScript==
// @name         国家开放大学课程视频后台播放
// @namespace    http://tampermonkey.net/
// @version      2.4
// @description  提升学习效率
// @author       TurbMZ
// @license      MIT
// @match        https://moodle.syxy.ouchn.cn/mod/*/view.php*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ouchn.cn
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    let cache_status = localStorage.getItem('_my_tamermonkey_status_');
    let cache_loop_status = localStorage.getItem('_my_tamermonkey_loop_status_');
    console.log('后台播放缓存状态',cache_status);
    console.log('后台循环缓存状态',cache_loop_status);
    if(cache_status === null) {
       cache_status= 0;
    } else {
       cache_status = JSON.parse(cache_status);
    }
    if(cache_loop_status === null){
       cache_loop_status = 0;
    } else {
       cache_loop_status = JSON.parse(cache_loop_status);
    }
    let loop_status = cache_loop_status || 0;
    let status = cache_status || 0;
    localStorage.setItem('_my_tamermonkey_status_', status);
    localStorage.setItem('_my_tamermonkey_loop_status_',loop_status);

    let _my_tamermonkey_speed_ = 1;
    const speedMap = [ 1, 1.5, 2, 4 ];
    const container = document.getElementById('page-content');
    const navDom = document.querySelector('.mobile_course');
    const cursess = navDom.querySelectorAll('.activity');
    const list = Array.from(cursess).filter(item=>{
        return Array.from(item.classList).includes('page') ||
               Array.from(item.classList).includes('url');
    })
    let videoDom = document.getElementsByTagName('VIDEO')[0];
    let tryTimes = 0; // 已重试次数
    const maxReTryTimes = 10; // 最大重试次数 200ms 重试一次
    // 播放下一个视频
    const nextPageVideo = function() {
        console.log('课程列表',list)
        let currentIndex = Array.from(list).findIndex((item,index)=>{
            return Array.from(item.classList).includes('current')
        });
        console.log('播放下一个视频',currentIndex)
        if(currentIndex>-1 && currentIndex != list.length-1){ // 有且 当前不是最后一个
            const nextLi = list[currentIndex+1]
            const alink = nextLi.querySelector('.aalink')
            alink&&alink.click()
        }
    }

    const init = function() {
        console.log("初始化插件功能", status, typeof status);
        container.style.position = 'relative';
        let btnText = status?'停用后台播放':'启用后台播放';
        let controlBox = document.createElement('DIV');
        let controlBtn = document.createElement('SPAN');
        let speedBtn = document.createElement('SPAN');
        let jopeToEndBtn = document.createElement('SPAN');
        let loopPlayBtn = document.createElement('SPAN');
        // 开关循环播放视频
        const loopHandler = function(e){
            loop_status = loop_status ? 0 : 1;
            localStorage.setItem('_my_tamermonkey_loop_status_',loop_status);
            e.target.innerText = loop_status?'关闭循环播放':'开启循环播放';
        };
        // 控制开关
        const controlHandler = function(e) {
            status = status ? 0: 1;
            localStorage.setItem('_my_tamermonkey_status_',status);
            let btnText = status?'停用后台播放':'启用后台播放';
            e.target.innerText = btnText;
        }
        // 调速
        const speedHandler = function(e) {
            let currentSpeedIndex = speedMap.findIndex(v=>_my_tamermonkey_speed_===v);
            if(currentSpeedIndex===speedMap.length-1){
                currentSpeedIndex = 0;
            }else{
                ++currentSpeedIndex;
            }
            _my_tamermonkey_speed_ = speedMap[currentSpeedIndex];
            videoDom.playbackRate = _my_tamermonkey_speed_;
            e.target.innerText = `快进>>${_my_tamermonkey_speed_}倍速`;
        }
        // 快进到最后
        const toEndHandler = function(e) {
            console.log('快进到最后');
            let duration = videoDom.duration;
            let toEndTime = Math.floor(duration-1);
            videoDom.currentTime = toEndTime;
            console.log(videoDom.currentTime);
        }
        //容器设置样式和id
        controlBox.id = 'myTampermonkeyControl';
        controlBox.style.cssText = 'position:absolute;right:0px;bottom:0px;display:flex;align-items:center;font-size:12px;';
        //控制按钮设置样式和id
        controlBtn.id = 'controlBtn';
        controlBtn.style.cssText = 'display:inline-block;padding: 4px 10px;border-radius:4px;margin-right:10px;cursor:pointer;background:#999;color:#fff;';
        controlBtn.innerText = btnText;
        speedBtn.id = 'speedBtn';
        speedBtn.style.cssText = 'display:inline-block;padding: 4px 10px;border-radius:4px;cursor:pointer;background:#999;color:#fff;margin-right:10px;';
        speedBtn.innerText = `快进>>${_my_tamermonkey_speed_}倍速`;
        jopeToEndBtn.style.cssText = 'display:inline-block;padding: 4px 10px;border-radius:4px;cursor:pointer;background:#999;color:#fff;';
        jopeToEndBtn.innerText = '跳到最后';
        loopPlayBtn.innerText = loop_status?'关闭循环播放':'开启循环播放';
        loopPlayBtn.style.cssText = 'display:inline-block;padding: 4px 10px;border-radius:4px;margin-right:10px;cursor:pointer;background:#999;color:#fff;';
        // 按钮添加事件
        loopPlayBtn.onclick = loopHandler;
        controlBtn.onclick= controlHandler;
        speedBtn.onclick = speedHandler;
        jopeToEndBtn.onclick = toEndHandler;
        controlBox.appendChild(loopPlayBtn);
        controlBox.appendChild(controlBtn);
        controlBox.appendChild(speedBtn);
        controlBox.appendChild(jopeToEndBtn);
        container.appendChild(controlBox);
        videoDom.addEventListener('ended', function () { //结束
            console.log("播放结束");
            console.log('是否循环播放',loop_status);
            if(loop_status) nextPageVideo()
            //controlBtn.click()
        }, false);
        document.addEventListener('visibilitychange', function() {
            if(!videoDom){
                videoDom = document.getElementsByTagName('VIDEO')[0];
            }
            var isHidden = document.hidden;
            console.log(document.visibilityState,isHidden,status); // window._my_tamermonkey_status_
            if (isHidden && status) {
                console.log(status);
                setTimeout(()=>{
                    videoDom.play();
                },200)
            }else if(isHidden){
                videoDom.pause();
            }
        });
        // 如开启后台播放,自动静音播放视频
        if(status) {
            videoDom.muted = 'muted';
            setTimeout(()=> {
                const playPromise = videoDom.play();
                if (playPromise !== undefined) {
                    playPromise.then(res => {
                        // Automatic playback started!
                        // Show playing UI.
                    }).catch(error => {
                        // Auto-play was prevented
                        // Show paused UI.
                    });
                }
            },100)
        }
    }
    if(videoDom){ // 有视频
        console.log('有视频');
        init();
    }else{ // 视频未加载,重试
        console.log('视频未加载');
        let timer = null;
            timer = setInterval(()=>{
                console.log('轮询');
                if(tryTimes >= maxReTryTimes){ // 重试最后都没有找到视频
                    clearInterval(timer);
                    timer = null;
                    if(loop_status){ // 如果是循环播放,则说明是文本课程,直接跳转下一个课程
                        console.log('文本课程,直接跳转下一个课程')
                        setTimeout(()=>{
                            nextPageVideo()
                        },2000)
                    }
                    return;
                }
                videoDom = document.getElementsByTagName('VIDEO')[0];
                if(videoDom){
                    try{
                        init();
                        clearInterval(timer);
                        timer = null;
                        return;
                    }catch(err){
                        clearInterval(timer);
                        timer = null;
                        console.log(err);
                    }
                }
                tryTimes++
            },200)
    }

    // Your code here...
})();