雨课堂课件PDF下载工具

在雨课堂页面自动生成PDF版本课件提供下载

当前为 2021-03-27 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

/* globals jspdf UPNG */

// ==UserScript==
// @name         Rain Classroom PDF Direct Download
// @name:zh-CN   雨课堂课件PDF下载工具
// @namespace    https://www.pizyds.com/
// @version      1.0.3
// @description  Automatic generation of direct download PDF on Rain Classroom
// @description:zh-CN 在雨课堂页面自动生成PDF版本课件提供下载
// @author       PillarsZhang
// @homepage     https://www.pizyds.com/rain-classroom-pdf-direct-download
// @license      MIT
// @match        https://www.yuketang.cn/v2/web/*
// @icon         https://www.yuketang.cn/static/images/favicon.ico
// @require      https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/pako/2.0.3/pako.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/upng-js/2.1.0/UPNG.min.js
// @resource     pizyds_iconfont_css https://at.alicdn.com/t/font_2448118_l5d66dc50k9.css
// @grant        GM_getResourceText
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';
    console.log("雨课堂课件PDF下载工具:已载入");
    //jsPDF用于PDF生成,UPNG、pako用于PNG的反交错和压缩
    const {jsPDF} = jspdf;

    //下载的图标,感谢iconfont
    const pizyds_iconfont_css = GM_getResourceText("pizyds_iconfont_css");
    GM_addStyle(pizyds_iconfont_css);

    //实时查找PPT窗口
    setInterval(()=>{
        var el_dialog = find_basePPTDialog();
        el_dialog && add_button_download(el_dialog);
    },500);

    //按钮触发PDF生成逻辑
    function download_process(el_dialog){
        var url_slides = get_url_slides(el_dialog);
        if (url_slides.length > 0){
            refreshProcessStatus("处理图片...");
            image_process(url_slides)
                .then(img_list => {
                refreshProcessStatus("生成PDF...");
                var ppt_name = document.getElementsByClassName("ppt_name")[0].innerText;
                var filename = ppt_name + ".pdf";
                pdf_process(img_list, filename);
                refreshProcessStatus("下载课件");
            })
        } else{
            alert("雨课堂课件PDF下载工具:没有提取到图片");
        }
    }

    //第一步-借助UPNG,进行图片下载与反交错、压缩处理
    function image_process(url_slides){
        var promiseList = new Array(url_slides.length);
        var finished_num = 0;
        var count_finished_num = (index) => {
            var processStatus = `${++finished_num}/${url_slides.length}`;
            refreshProcessStatus(`处理图片(${processStatus})`);
            console.log(`${processStatus} - 第${index+1}页 - ${url_slides[index]} - finished`);
        }
        for (let i = 0; i < url_slides.length; i++){
            promiseList[i] = fetch(url_slides[i]).then(response => {
                return response.arrayBuffer();
            }).then(arrayBuffer_origin => {
                var img = UPNG.decode(arrayBuffer_origin);
                var rgba = UPNG.toRGBA8(img);
                var arrayBuffer_compress = UPNG.encode(rgba, img.width, img.height, 0);
                count_finished_num(i);
                return {unit8: new Uint8Array(arrayBuffer_compress), width: img.width, height: img.height};
            }).catch(err => {
                console.error(err);
                alert("雨课堂课件PDF下载工具:图像处理出错");
            });
        }
        return Promise.all(promiseList);
    }

    //第二步-借助jsPDF,进行PDF的生成
    function pdf_process(img_list, filename){
        var doc = new jsPDF({
            orientation: "landscape",
            unit: "px",
            format: [img_list[0].width, img_list[0].height],
            hotfixes: ["px_scaling"]
        });
        doc.addImage(img_list[0].unit8, 'PNG', 0, 0, img_list[0].width, img_list[0].height);
        for (let i = 1; i < img_list.length; i++){
            doc.addPage([img_list[i].width, img_list[i].height], "landscape");
            doc.addImage(img_list[i].unit8, 'PNG', 0, 0, img_list[i].width, img_list[i].height);
        }
        doc.save(filename);
    }

    //按钮文本刷新
    function refreshProcessStatus(processStatus){
        var el_download = document.getElementsByClassName("pizyds_download")[0];
        el_download.innerHTML = `<i class="iconfont icon-pizyds-rain-down-xiazai"></i> ${processStatus}`;
    }

    //查找PPT窗口
    function find_basePPTDialog(){
        var el_dialogs = document.getElementsByClassName("basePPTDialog");
        if (el_dialogs.length == 1){
            return el_dialogs[0];
        } else{
            return false;
        }
    }

    //PPT图片链接提取
    function get_url_slides(el_dialog){
        try{
            var el_swiper = el_dialog.getElementsByClassName("pptSwiper")[0];
            var el_slides = el_swiper.getElementsByClassName("swiper-slide");
            var url_slides = new Array(el_slides.length);
            for(let i = 0; i < el_slides.length; i++){
                url_slides[i] = el_slides[i].getElementsByTagName("img")[0].src;
            }
            return url_slides;
        } catch(err){
            return new Array();
        }
    }

    //按钮注入
    function add_button_download(el_dialog){
        var el_header = el_dialog.getElementsByClassName("layout_header")[0];
        if (el_header.getElementsByClassName("pizyds_download").length == 0){
            var el_download = create_node_from_html(`<span class="print pizyds_download" style="right:120px">
              <i class="iconfont icon-pizyds-rain-down-xiazai"></i> 下载课件</span>`);
            el_download.onclick = () => download_process(el_dialog);
            el_header.appendChild(el_download);
            console.log("按钮注入成功");
            return true;
        } else{
            return false;
            console.log("按钮注入失败");
        }
    }

    //HTML字符串转节点
    function create_node_from_html(html){
        let template = `<div class='child'>${html}</div>`;
        let tempNode = document.createElement('div');
        tempNode.innerHTML = template;
        return tempNode.firstChild;
    }
})();