edX网课下载器

一键下载edX网课视频和字幕,并保存为相同的文件名(除了文件后缀)。<https://github.com/jks-liu/edx-downloader.monkey.js>

  1. // ==UserScript==
  2. // @name edX Downloader
  3. // @name:zh-CN edX网课下载器
  4. // @name:zh-TW edX網課下載器
  5. // @name:ja edXダウンローダー
  6. // @namespace https://github.com/jks-liu/edx-downloader.monkey.js
  7. // @supportURL https://github.com/jks-liu/edx-downloader.monkey.js
  8. // @version 2.0.1
  9. // @description Download edX course mp4 and srt in one click, and save them as same file name (except the file suffix). <https://github.com/jks-liu/edx-downloader.monkey.js>
  10. // @description:zh-CN 一键下载edX网课视频和字幕,并保存为相同的文件名(除了文件后缀)。<https://github.com/jks-liu/edx-downloader.monkey.js>
  11. // @description:zh-TW 一鍵下載edX網課視頻和字幕,並保存爲相同的文件名(除了文件後綴)。<https://github.com/jks-liu/edx-downloader.monkey.js>
  12. // @description:ja edXオンラインコースのビデオと字幕をワンクリックでダウンロードし、同じファイル名で保存します(ファイル拡張子を除く)。<https://github.com/jks-liu/edx-downloader.monkey.js>
  13. // @author Jks Liu (https://github.com/jks-liu)
  14. // @license MIT
  15. // @match https://edx.org/*
  16. // @match https://www.edx.org/*
  17. // @match https://learning.edx.org/*
  18. // @match https://courses.edx.org/*
  19. // @icon https://www.google.com/s2/favicons?domain=edx.com
  20. // @grant GM_download
  21. // @grant GM_setValue
  22. // @grant GM_getValue
  23. // ==/UserScript==
  24.  
  25. // https://stackoverflow.com/questions/14308588/simple-jquery-selector-only-selects-first-element-in-chrome
  26. // If jQuery isn't present on the webpage, and of course no other code assigns something to $, Chrome's JS console assigns $ a shortcut to document.querySelector().
  27. // You can achieve what you want with $$(), which is assigned by the console a shortcut to document.querySelectorAll().
  28.  
  29. // TODO: Add real title
  30. // TODO: Download in floder
  31. // TODO: label of already download
  32. // TODO: Process bar
  33. // TODO: Count of current course
  34. // TODO: Download html
  35. // TODO: Auto goto nextpage and download
  36.  
  37. // jks_ is the namespace
  38.  
  39. // The structure is as below
  40. // learning.edx.org -> Containing title
  41. // ifram, courses.edx.org -> Containing video address
  42.  
  43.  
  44. function jks_is_host(site) {
  45. return window.location.href.indexOf(site) != -1
  46. }
  47.  
  48.  
  49. function jks_learning_edu_org() {
  50. 'use strict';
  51.  
  52. // Original page has no jQuery
  53. // $$ can be used in broswer, but not here
  54. // below line from https://github.com/akhodakivskiy/VimFx/issues/841#issuecomment-263197162
  55. const $$ = (...args) => Array.from(document.querySelectorAll(...args));
  56.  
  57. var checkExist = setInterval(function() {
  58. let all_titles = $$("ol.list-unstyled a");
  59. let all_buttons = $$("div.sequence-navigation-tabs > button");
  60. if (all_titles.length && all_buttons.length) {
  61. clearInterval(checkExist);
  62.  
  63. /// Get course meta data
  64. let course_name = all_titles[1].text;
  65. let paragraph_name = all_titles[2].text;
  66. let all_sections = all_buttons.map(button=>button.title);
  67. let active_section_index = all_buttons.findIndex(button=>button.classList.contains("active"));
  68. let active_section = all_sections[active_section_index];
  69. console.log("jks edx names", course_name, paragraph_name, active_section);
  70. GM_setValue("names", [course_name, paragraph_name, active_section, active_section_index]);
  71.  
  72. /// Register action to detect meta data change
  73. let class_context = $$("div.sequence-navigation-tabs")[0];
  74. // https://stackoverflow.com/questions/38861601/how-to-only-trigger-parent-click-event-when-a-child-is-clicked/38861760
  75. class_context.addEventListener('click', function(event) {
  76. let button = event.target;
  77. while (button.nodeName != "BUTTON") {
  78. button = button.parentElement;
  79. }
  80.  
  81. let new_section = button.title;
  82. let new_section_index = all_buttons.findIndex(b => b===button);
  83. GM_setValue("names", [course_name, paragraph_name, new_section, new_section_index]);
  84. }, true);
  85. }
  86. }, 100); // check every 100ms
  87. }
  88.  
  89. function jks_courses_edx_org() {
  90. 'use strict';
  91.  
  92. // Original page has jQuery
  93.  
  94. // The content is under an iframe
  95. // So @match is https://courses.edx.org, not https://learning.edx.org
  96. // Can not use `window.$` as below ajax, don't know why
  97. let video = $('.video-download-button')[0];
  98. let video_url = video.href;
  99.  
  100. // Create a button
  101. let download_all_button = document.createElement("div");
  102. download_all_button.innerHTML = `
  103. <label>
  104. <input id="jks_checkbox_mp4" type="checkbox", checked=true>
  105. <span>*.mp4</span>
  106. <input id="jks_checkbox_srt" type="checkbox", checked=true>
  107. <span>*.srt</span>
  108. <input id="jks_checkbox_txt" type="checkbox">
  109. <span>*.txt</span>
  110. </label>
  111. <button id="jks_button">Download All</button>
  112. <form>
  113. <label for="jks_input_folder">Folder</label>
  114. <input type="text" id="jks_input_folder" name="jks_input_folder" size="72">
  115. <label for="jks_input_mp4">*.mp4 location:</label>
  116. <input type="text" id="jks_input_mp4" name="jks_input_mp4" size="72">
  117. <label for="jks_input_srt">*.srt location:</label>
  118. <input type="text" id="jks_input_srt" name="jks_input_srt" size="72">
  119. <label for="jks_input_txt">*.txt location:</label>
  120. <input type="text" id="jks_input_txt" name="jks_input_txt" size="72">
  121. </form>
  122. `;
  123. // Video download event is at parent element
  124. // So add button after parent, or it's button click will be hide by parent
  125. // Add 2 other parentElement to widen input box
  126. video.parentElement.parentElement.parentElement.append(download_all_button)
  127.  
  128. // Cannot use $, don't know why
  129. let srt_url = document.querySelector("li.transcript-option a").href;
  130.  
  131. // Use $ instead of window.$, previously window.$ is ok, don't know why
  132. $.ajax({
  133. url: document.querySelector("li.transcript-option a").href,
  134. type: "HEAD",
  135. success: function(res, status, xhr) {
  136. let srt_header = xhr.getResponseHeader("content-disposition");
  137. // attachment; filename="02_TinyML_C02_03-01-01-en.srt" => 02_TinyML_C02_03-01-01-en.srt
  138. // let srt_file_name = srt_header.slice(22, -1);
  139. // let mp4_file_name = srt_file_name.slice(0, -4)+".mp4";
  140. // let txt_file_name = srt_file_name.slice(0, -4)+".txt";
  141.  
  142. let names = GM_getValue("names", ["edx-unknown-course", "edx-unknown-paragraph", "edx-unknown-section"]);
  143. let folder_name = names[0]+"/"+names[1]
  144. let base_name = String(names[3]).padStart(2, "0")+"-"+names[2];
  145. let mp4_file_name = base_name + ".mp4";
  146. let srt_file_name = base_name + ".srt";
  147. let txt_file_name = base_name + ".txt";
  148. $("#jks_input_folder")[0].value = folder_name;
  149. $("#jks_input_mp4")[0].value = mp4_file_name;
  150. $("#jks_input_srt")[0].value = srt_file_name;
  151. $("#jks_input_txt")[0].value = txt_file_name;
  152.  
  153. $("#jks_button").click(function() {
  154. if ($("#jks_checkbox_mp4")[0].checked) {
  155. // video is CORS (Cross-origin resource)
  156. // Set tampermonkey `Download Mode` option to `Browser API`
  157. // https://www.tampermonkey.net/faq.php?ext=dhdg#Q302
  158. GM_download(video_url, $("#jks_input_folder")[0].value+"/"+$("#jks_input_mp4")[0].value);
  159. }
  160.  
  161. if ($("#jks_checkbox_srt")[0].checked) {
  162. GM_download(srt_url, $("#jks_input_folder")[0].value+"/"+$("#jks_input_srt")[0].value);
  163. }
  164.  
  165. if ($("#jks_checkbox_txt")[0].checked) {
  166. // Txt and srt and the same file
  167. GM_download(srt_url, $("#jks_input_folder")[0].value+"/"+$("#jks_input_txt")[0].value);
  168. }
  169. })
  170. }
  171. })
  172. }
  173.  
  174. (function() {
  175. 'use strict';
  176.  
  177. if (jks_is_host("courses.edx.org")) {
  178. jks_courses_edx_org();
  179. }
  180.  
  181. if (jks_is_host("learning.edx.org")) {
  182. jks_learning_edu_org();
  183. }
  184. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址