Greasy Fork is available in English.

云端课堂回放下载

云端课堂所有回放解析,一次性下载所有大班课回放

// ==UserScript==
// @name         云端课堂回放下载
// @namespace    http://tampermonkey.net/
// @license MIT
// @version      0.3
// @description  云端课堂所有回放解析,一次性下载所有大班课回放
// @author       You
// @match        https://e62580258.at.baijiayun.com/web/course/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=baijiayun.com
// @grant        none
// @require      https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.5.1.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.min.js
// @require      https://unpkg.com/layui@2.9.7/dist/layui.js
// @require      https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/qs/6.11.2/qs.min.js
// ==/UserScript==
var i = function (e, t) {
    var r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var n = r.indexOf(e.charAt(t));
    if (-1 === n) throw "Cannot decode base64";
    return n
};
// 解密视频地址
var decryptVideo = function(e) {
    if ("" === e || 0 !== e.indexOf("bjcloudvod://"))
        return "";
    var t = (e = e.slice("bjcloudvod://".length, e.length).replace(/-/g, "+").replace(/_/g, "/")).length % 4;
    2 === t ? e += "==" : 3 === t && (e += "=");
    var n = (e = bb(e)).charCodeAt(0) % 8;
    e = e.slice(1, e.length);
    for (var i, a = [], s = 0; i = e[s]; s++) {
        var o = s % 4 * n + s % 3 + 1;
        a.push(String.fromCharCode(i.charCodeAt(0) - o))
    }
    return a.join("").replace("https:", "").replace("http:", "")
};
// 解密编码
var bb = function (e) {
    var t, n, r = 0,
        a = e.length,
        s = [];
    if (e = String(e), 0 === a) return e;
    if (a % 4 != 0) throw "Cannot decode base64";
    for ("=" === e.charAt(a - 1) && (r = 1, "=" === e.charAt(a - 2) && (r = 2), a -= 4), t = 0; t <
         a; t += 4) n = i(e, t) << 18 | i(e, t + 1) << 12 | i(e, t + 2) << 6 | i(e, t + 3), s.push(
        String.fromCharCode(n >> 16, n >> 8 & 255, 255 & n));
    switch (r) {
        case 1:
            n = i(e, t) << 18 | i(e, t + 1) << 12 | i(e, t + 2) << 6, s.push(String.fromCharCode(
                n >> 16, n >> 8 & 255));
            break;
        case 2:
            n = i(e, t) << 18 | i(e, t + 1) << 12, s.push(String.fromCharCode(n >> 16))
    }
    return s.join("")
};

// 下载服务器的MP4文件
function downloadMp4(filePath,fileName){
  fetch(filePath).then(res => res.blob()).then(blob => {
    const a = document.createElement('a');
    a.style.display = 'none'
    // 使用获取到的blob对象创建的url
    const url = window.URL.createObjectURL(blob);
    a.href = url;
    // 指定下载的文件名
    a.download = fileName;
    a.click();
    // 移除blob对象的url
    window.URL.revokeObjectURL(url);
  });
}
// 创建一个vue对象,用于保存一些数据
var vueApp = new Vue({
    el: '#vueApp',
    data: {
        // 科目数量
        kemuCountList:[],
        // 科目信息
        kemuInfoList: [],
        // 科目里的课程信息,根据title区分
        kechengInfoMap:{},
        // 最终结果,根据科目分组视频地址
        resultList:{}
    }
});
top.vueApp = vueApp;
(function() {
    console.log("云端课堂脚本3.0开始===========================");
    // 引入第三方CSS,使用文档参考
    var link = document.createElement('link');
    link.id='layuiCss';
    link.rel = 'stylesheet';
    link.href = 'https://unpkg.com/layui@2.9.7/dist/css/layui.css'; // 替换为你要引入的CSS文件的URL
    document.head.appendChild(link);

    async function 获取大班科目信息(){
        var config = {
            method: 'post',
            url: '/org/class_playback/getLongTermRoomList?page=1&page_size=60'
        };
        await axios(config).then(function (response) {
            vueApp.kemuInfoList = response.data?.data?.list||[];
            console.log('1. 获取大班科目信息')
            // 遍历所有大班课信息
            for(var i=0;i<vueApp.kemuInfoList.length;i++){
                console.log(`2.遍历大班科目信息,获取课程---------------当前进度${i+1}/${vueApp.kemuInfoList.length}`);
                var kemuInfo = vueApp.kemuInfoList[i];
                某科目回放(kemuInfo.playback_count,kemuInfo.room_id,kemuInfo.title)
            }
            console.log('2.课程获取完毕')
        }).catch(function (error) {
            console.log(error);
        });
    }
    /**
    * pageSize 页大小
    * roomId 教室id
    */
    async function 某科目回放(pageSize,roomId,title){
        var config = {
            method: 'post',
            url: '/org/class_playback/getLongTermList',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            data : {
                'page': 1,
                'page_size': pageSize,
                'room_id': roomId
            }
        };
        await axios(config).then(function (response) {
            vueApp.kechengInfoMap[title] = response.data?.data?.list||[];
        }).catch(function (error) {
            console.log(error);
        });
    }
    function 获取视频地址(title,room_id,player_token,session_id,jindu){
        if(!room_id){
            return false;
        }
        var config = {
            method: 'get',
            url: `/web/playback/getPlayInfo?room_id=${room_id}&token=${player_token}&session_id=${session_id}&user_name=default&user_number=0&use_encrypt=1&render=jsonp&skip_encrypt=0`,
        };
        axios(config).then(function (response) {
            vueApp.resultList[title] = vueApp.resultList[title] || [];
            var responseBody = response.data;
            // 都获取到链接了还等啥,直接下载吧
            // 默认获取画质最高的
            var play_info = responseBody.data['play_info'];
            console.log(play_info);
            var videoUrl = play_info['superHD'] || play_info['high'] || play_info['720p'] || play_info['low'];
            videoUrl = videoUrl.cdn_list[0].enc_url;
            console.log(videoUrl);
            videoUrl = "http:" + decryptVideo(videoUrl);
            // 视频名称
            var fileName = `${responseBody.data.video_info.title}.${responseBody.data.format}`;
            vueApp.resultList[title].push({"fileName":fileName,"videoUrl":videoUrl});
            console.log(`进度:${jindu} 开始收集视频-视频名称${fileName},视频地址解析后${videoUrl}`);
        }).catch(function (error) {
            console.log(error);
        });
    }
    // 初始化方法
    function initFunction() {
        console.log('页面加载完成,开始执行脚本');
        // 1.添加我的菜单到左侧菜单树
        var myMenu = `<div class="bjy-tab-item" id="myMenu" onclick="downloadAllFile()"><i class="bjy-icon bjy-video"></i>专迪下载</div>`;
        $(".bjy-tab").append(myMenu);
        console.log("1.添加我的菜单到左侧菜单树");
        获取大班科目信息();
        console.log('某科目回放 收集完毕')
        console.log(vueApp.kechengInfoMap);
        // 我认为10s后,所有的科目都收集齐了,这时候就该下载了,不要轻易刷新该网页
        layer.msg("倒计时10s,后再点击【下载】");
        let countdown = 10;
        const timerId = setInterval(() => {
            console.log(countdown);
            countdown--;
            if (countdown === 0) {
                clearInterval(timerId);
                console.log('Countdown finished!');
                return false;
            }
            layer.msg(countdown);
        }, 1000);
        setTimeout(function(){
            console.log(`3.获取视频地址`);
            for(var title in vueApp.kechengInfoMap){
                // 获取视频地址
                var kechengInfoList = vueApp.kechengInfoMap[title];
                for(var i=0;i<kechengInfoList.length;i++){
                    获取视频地址(title,kechengInfoList[i].room_id,kechengInfoList[i].player_token,kechengInfoList[i].session_id,`${i+1}/${kechengInfoList.length}`);
                }
            }
        },10*1000)
    }

    window.addEventListener('load', initFunction);

    // 下载所有文件
    top.window['downloadAllFile'] = function(){
        // 开始校验数据是否已完全被下载
        var allFileUrlFlag = true;
        for(var title in vueApp.kechengInfoMap){
            // 获取视频地址
            if(vueApp.kechengInfoMap[title].length!=vueApp.resultList[title].length){
                allFileUrlFlag = false;
                break
            }
        }
        if(!allFileUrlFlag){
            layer.msg("数据还没收集完毕,稍等下再来试试吧");
            return false;
        }
        layer.msg("准备开始下载,坐稳了,要出发了");
        for(var title in vueApp.resultList){
            // 获取视频地址
            for(var i=0;i<vueApp.resultList[title].length;i++){
                var videoInfo = vueApp.resultList[title][i];
                console.log(`下载视频-title:${title},进度:${i+1}/${vueApp.resultList[title].length}`)
                downloadMp4(videoInfo.videoUrl,videoInfo.fileName);
            }
        }
    }
})();