雨课堂课件PDF下载工具

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

目前为 2021-03-27 提交的版本。查看 最新版本

  1. /* globals jspdf UPNG */
  2.  
  3. // ==UserScript==
  4. // @name Rain Classroom PDF Direct Download
  5. // @name:zh-CN 雨课堂课件PDF下载工具
  6. // @namespace https://www.pizyds.com/
  7. // @version 1.0.5
  8. // @description Automatic generation of direct download PDF on Rain Classroom
  9. // @description:zh-CN 在雨课堂页面自动生成PDF版本课件提供下载
  10. // @author PillarsZhang
  11. // @homepage https://www.pizyds.com/rain-classroom-pdf-direct-download
  12. // @license MIT
  13. // @match https://www.yuketang.cn/*
  14. // @icon https://www.yuketang.cn/static/images/favicon.ico
  15. // @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js
  16. // @require https://cdnjs.cloudflare.com/ajax/libs/pako/2.0.3/pako.min.js
  17. // @require https://cdnjs.cloudflare.com/ajax/libs/upng-js/2.1.0/UPNG.min.js
  18. // @resource pizyds_iconfont_css https://at.alicdn.com/t/font_2448118_l5d66dc50k9.css
  19. // @grant GM_getResourceText
  20. // @grant GM_addStyle
  21. // ==/UserScript==
  22.  
  23. (function() {
  24. 'use strict';
  25. console.log("雨课堂课件PDF下载工具:已载入");
  26. //jsPDF用于PDF生成,UPNG、pako用于PNG的反交错和压缩
  27. const {jsPDF} = jspdf;
  28.  
  29. //下载的图标,感谢iconfont
  30. const pizyds_iconfont_css = GM_getResourceText("pizyds_iconfont_css");
  31. GM_addStyle(pizyds_iconfont_css);
  32.  
  33. //实时查找PPT窗口
  34. setInterval(()=>{
  35. var el_dialog = find_basePPTDialog();
  36. check_url && el_dialog && add_button_download(el_dialog);
  37. },200);
  38.  
  39. //更改为内部校验链接,因为大量ajax页面跳转的存在
  40. function check_url(){
  41. return /https:\/\/www\.yuketang\.cn\/v2\/web\/student.*/.test(window.location.href)
  42. }
  43.  
  44. //按钮触发PDF生成逻辑
  45. function download_process(el_dialog){
  46. var url_slides = get_url_slides(el_dialog);
  47. if (url_slides.length > 0){
  48. refreshProcessStatus("处理图片...");
  49. image_process(url_slides)
  50. .then(img_list => {
  51. refreshProcessStatus("生成PDF...");
  52. var ppt_name = document.getElementsByClassName("ppt_name")[0].innerText;
  53. var filename = ppt_name + ".pdf";
  54. pdf_process(img_list, filename);
  55. refreshProcessStatus("下载课件");
  56. })
  57. } else{
  58. alert("雨课堂课件PDF下载工具:没有提取到图片");
  59. }
  60. }
  61.  
  62. //第一步-借助UPNG,进行图片下载与反交错、压缩处理
  63. function image_process(url_slides){
  64. var promiseList = new Array(url_slides.length);
  65. var finished_num = 0;
  66. var count_finished_num = (index) => {
  67. var processStatus = `${++finished_num}/${url_slides.length}`;
  68. refreshProcessStatus(`处理图片(${processStatus})`);
  69. console.log(`${processStatus} - ${index+1}页 - ${url_slides[index]} - finished`);
  70. }
  71. for (let i = 0; i < url_slides.length; i++){
  72. promiseList[i] = fetch(url_slides[i]).then(response => {
  73. return response.arrayBuffer();
  74. }).then(arrayBuffer_origin => {
  75. var img = UPNG.decode(arrayBuffer_origin);
  76. var rgba = UPNG.toRGBA8(img);
  77. var arrayBuffer_compress = UPNG.encode(rgba, img.width, img.height, 0);
  78. count_finished_num(i);
  79. return {unit8: new Uint8Array(arrayBuffer_compress), width: img.width, height: img.height};
  80. }).catch(err => {
  81. console.error(err);
  82. alert("雨课堂课件PDF下载工具:图像处理出错");
  83. });
  84. }
  85. return Promise.all(promiseList);
  86. }
  87.  
  88. //第二步-借助jsPDF,进行PDF的生成
  89. function pdf_process(img_list, filename){
  90. var doc = new jsPDF({
  91. orientation: "landscape",
  92. unit: "px",
  93. format: [img_list[0].width, img_list[0].height],
  94. hotfixes: ["px_scaling"]
  95. });
  96. doc.addImage(img_list[0].unit8, 'PNG', 0, 0, img_list[0].width, img_list[0].height);
  97. for (let i = 1; i < img_list.length; i++){
  98. doc.addPage([img_list[i].width, img_list[i].height], "landscape");
  99. doc.addImage(img_list[i].unit8, 'PNG', 0, 0, img_list[i].width, img_list[i].height);
  100. }
  101. doc.save(filename);
  102. }
  103.  
  104. //按钮文本刷新
  105. function refreshProcessStatus(processStatus){
  106. var el_download = document.getElementsByClassName("pizyds_download")[0];
  107. el_download.innerHTML = `<i class="iconfont icon-pizyds-rain-down-xiazai"></i> ${processStatus}`;
  108. }
  109.  
  110. //查找PPT窗口
  111. function find_basePPTDialog(){
  112. var el_dialogs = document.getElementsByClassName("basePPTDialog");
  113. if (el_dialogs.length == 1){
  114. return el_dialogs[0];
  115. } else{
  116. return false;
  117. }
  118. }
  119.  
  120. //PPT图片链接提取
  121. function get_url_slides(el_dialog){
  122. try{
  123. var el_swiper = el_dialog.getElementsByClassName("pptSwiper")[0];
  124. var el_slides = el_swiper.getElementsByClassName("swiper-slide");
  125. var url_slides = new Array(el_slides.length);
  126. for(let i = 0; i < el_slides.length; i++){
  127. url_slides[i] = el_slides[i].getElementsByTagName("img")[0].src;
  128. }
  129. return url_slides;
  130. } catch(err){
  131. return new Array();
  132. }
  133. }
  134.  
  135. //按钮注入
  136. function add_button_download(el_dialog){
  137. var el_header = el_dialog.getElementsByClassName("layout_header")[0];
  138. if (el_header.getElementsByClassName("pizyds_download").length == 0){
  139. var el_download = create_node_from_html(`<span class="print pizyds_download" style="right:120px">
  140. <i class="iconfont icon-pizyds-rain-down-xiazai"></i> 下载课件</span>`);
  141. el_download.onclick = () => download_process(el_dialog);
  142. el_header.appendChild(el_download);
  143. console.log("按钮注入成功");
  144. return true;
  145. } else{
  146. return false;
  147. console.log("按钮注入失败");
  148. }
  149. }
  150.  
  151. //HTML字符串转节点
  152. function create_node_from_html(html){
  153. let template = `<div class='child'>${html}</div>`;
  154. let tempNode = document.createElement('div');
  155. tempNode.innerHTML = template;
  156. return tempNode.firstChild;
  157. }
  158. })();