Canvas Media Gallery Grabber

No more buffering!!

  1. // ==UserScript==
  2. // @name Canvas Media Gallery Grabber
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description No more buffering!!
  6. // @author Pikachu
  7. // @include *
  8. // @run-at document-start
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11.  
  12. // DISCLAIMER: This tool is intended only for accessing authorized content offline.
  13. // You shall NOT use this tool to COPY or REDISTRIBUTE any of the downloaded content.
  14.  
  15. // Have fun reading the docs ~
  16. // https://developer.kaltura.com/api-docs/Deliver-and-Distribute-Media/playManifest-streaming-api.html
  17.  
  18. // Update Log:
  19. // 0.2: Now support downloading video media with kaltura player from any page on Canvas besides playlists.
  20. // File names are finally, finally sorted out. Now downloaded file names will be automatically set to video name!
  21.  
  22.  
  23. function addbutton(video_title, dl_link) {
  24. // Add button
  25. var link_div = document.createElement("div");
  26. link_div.innerHTML = '<a href="' + dl_link + '" download="' + video_title + '.mp4">Download ' + video_title + '</a>';
  27. link_div.setAttribute("id", "download_button_container");
  28.  
  29. document.getElementById("mediaContainer").parentNode.insertBefore(link_div, document.getElementById("mediaContainer"));
  30.  
  31. //GM_addStyle('.download_button_container { position: absolute; top: 0px; right: 0px }')
  32. }
  33.  
  34. function getassets(video_title, video_src, entry_id) {
  35. // Retrive ks
  36. var ks = video_src.substr(video_src.indexOf("&ks=")+4, video_src.indexOf("&", video_src.indexOf("&ks=")+4)-video_src.indexOf("&ks=")-4);
  37. //console.log("ks=",ks);
  38.  
  39. var video_info = {
  40. "p":null,
  41. "sp":null,
  42. "entryId":entry_id,
  43. "flavorIds":null,
  44. "format":null,
  45. "protocol":null
  46. };
  47.  
  48. for (var key in video_info) {
  49. var value = video_src.substr(
  50. video_src.indexOf(key+"/")+key.length+1,
  51. video_src.indexOf("/", video_src.indexOf(key+"/")+key.length+1)-video_src.indexOf(key+"/")-key.length-1
  52. );
  53. video_info[key] = value;
  54. };
  55.  
  56. // Modify format
  57. video_info["format"] = "url"
  58.  
  59. //console.log(video_info["entryId"]);
  60.  
  61. // Get the best resolution
  62. var assets = GM_xmlhttpRequest({
  63. method: "GET",
  64. url: "https://www.kaltura.com/api_v3/service/flavorasset/action/getWebPlayableByEntryId?entryId="+video_info["entryId"]+"&ks="+ks,
  65. onerror: function(r) {
  66. console.log("Error when retriving assets.", "Detail: \n", r.responseText);
  67. },
  68.  
  69. onload: function(r) {
  70. var assets = parseassets(r.responseText);
  71.  
  72. var chosenasset = null;
  73. var chosenassetprop = 0;
  74.  
  75. // Choose the asset with highest resolution
  76. for (var entryid in assets) {
  77. if (assets[entryid]["width"] * assets[entryid]["height"] > chosenassetprop) {
  78. chosenasset = entryid;
  79. chosenassetprop = assets[entryid]["width"] * assets[entryid]["height"];
  80. }
  81. }
  82.  
  83. video_info["flavorIds"] = assets[chosenasset]["id"];
  84.  
  85. var dl_link = "https://cdnapisec.kaltura.com/p/"+video_info["p"]+"/sp/"+video_info["sp"]+"/playManifest";
  86. for (key in video_info) {
  87. if (key != "p" && key != "sp") {
  88. dl_link += "/"+key+"/"+video_info[key];
  89. }
  90. }
  91.  
  92. // Append header
  93. dl_link += "/name/" + encodeURI(video_title) + ".mp4?ks=" + ks;
  94. getdlink(video_title, dl_link);
  95. }
  96. })
  97. }
  98.  
  99. function getdlink(video_title, req) {
  100. var dlink = GM_xmlhttpRequest({
  101. method: "GET",
  102. url: req,
  103. onerror: function(r) {
  104. console.log("Error when retriving download link.", "Detail: \n", r.responseText);
  105. },
  106.  
  107. onreadystatechange: function(r) {
  108. if (this.readyState == this.HEADERS_RECEIVED) {
  109. var final_dlink = req;
  110. if (r.status == 200) {
  111. final_dlink = r.finalUrl;
  112. }
  113. // Stop loading before it actually loads
  114. dlink.abort();
  115. // Black magic. Somehow we cannot have any non alphanumeric characters besides underscore in the name, or we will get 404.
  116. final_dlink = final_dlink.replace("/name/a.mp4", "/name/" + video_title.replace(/[\W_]+/g, "_") + ".mp4");
  117. console.log(video_title + ": " + final_dlink,"\n");
  118. addbutton(video_title, final_dlink);
  119. }
  120. }
  121. })
  122. }
  123.  
  124. function parseassets(assets) {
  125. //console.log(assets);
  126. var parser = new DOMParser();
  127. var xmlassets = parser.parseFromString(assets, "text/xml");
  128.  
  129. var asset = {};
  130.  
  131. for (var i=0; i<xmlassets.getElementsByTagName("item").length; i++) {
  132. var item_dict = {}
  133. var item = xmlassets.getElementsByTagName("item")[i]
  134.  
  135. for (var ii=0; ii<item.getElementsByTagName("*").length; ii++) {
  136. var tag = item.getElementsByTagName("*")[ii];
  137. item_dict[tag.tagName] = tag.textContent;
  138. }
  139.  
  140. if ("id" in item_dict) {
  141. asset[item_dict["id"]] = item_dict;
  142. }
  143. }
  144.  
  145. return asset;
  146. }
  147.  
  148.  
  149. (function() {
  150. 'use strict';
  151.  
  152. // Your code here...
  153.  
  154. var video_src = null;
  155. var entry_id = null;
  156. var video_title = null;
  157.  
  158. if (window.top != window.self) {
  159. var checkExist = setInterval(function() {
  160. if (document.getElementById("kplayer_ifp") != null) {
  161. var player_if = document.getElementById("kplayer_ifp").contentWindow;
  162. if (player_if.document.getElementById("pid_kplayer") != null) {
  163. video_src = player_if.document.getElementById("pid_kplayer").getAttribute("src");
  164. entry_id = player_if.document.getElementById("pid_kplayer").getAttribute("kentryid");
  165. if (video_src != null && entry_id != null) {
  166. //console.log("src=" + video_src);
  167. clearInterval(checkExist);
  168. if (player_if.document.querySelectorAll('[data-plugin-name="titleLabel"]').length > 0) {
  169. video_title = player_if.document.querySelectorAll('[data-plugin-name="titleLabel"]')[0].getAttribute("title").trim();
  170. }
  171. else if (document.getElementsByClassName("entryTitle").length > 0) {
  172. video_title = document.getElementsByClassName("entryTitle")[0].textContent.trim();
  173. }
  174. getassets(video_title, video_src, entry_id);
  175. }
  176. }
  177. } else {
  178. //console.log("still waiting...")
  179. }
  180. }, 1000); // check every 1000ms
  181. } else {
  182. // Not running in iframe
  183. }
  184.  
  185.  
  186. })();

QingJ © 2025

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