ED-Puzzle Hack

An Ed-Puzzle Hack with a LOT of Features!!!!!!!

  1. // ==UserScript==
  2. // @name ED-Puzzle Hack
  3. // @namespace https://github.com/longkidkoolstar
  4. // @description An Ed-Puzzle Hack with a LOT of Features!!!!!!!
  5. // @author longkidkoolstar
  6. // @version 0.2.1
  7. // @icon https://th.bing.com/th/id/OIP.3LKllA9fA7DTJ4Kb92LbowHaHa?rs=1&pid=ImgDetMain
  8. // @match *://edpuzzle.com/lti/*
  9. // @match *://edpuzzle.com/assignments/*
  10. // @match *://edpuzzle.com/media/*
  11. // @match *://youtube.com/embed*
  12. // @match *://youtube-nocookie.com/embed*
  13. // @grant none
  14. // @license GPL
  15. // ==/UserScript==
  16.  
  17. /*
  18. This program is free software: you can redistribute it and/or modify
  19. it under the terms of the GNU General Public License as published by
  20. the Free Software Foundation, either version 3 of the License, or
  21. (at your option) any later version.
  22.  
  23. This program is distributed in the hope that it will be useful,
  24. but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. GNU General Public License for more details.
  27.  
  28. You should have received a copy of the GNU General Public License
  29. along with this program. If not, see <https://www.gnu.org/licenses/>.
  30. */
  31.  
  32. //Copyright (C) [2024] [longkidkoolstar]
  33. // This script is based on the original code from the edpuzzle-answers project by ading2210
  34. // See: https://github.com/ading2210/edpuzzle-answers
  35.  
  36.  
  37.  
  38. var popup = null;
  39. var base_url;
  40. if (typeof document.dev_env != "undefined") {
  41. base_url = document.dev_env;
  42. }
  43. else {
  44. //get resources off of github to not inflate the jsdelivr stats
  45. base_url = "https://raw.githubusercontent.com/ading2210/edpuzzle-answers/main";
  46. }
  47.  
  48. function http_get(url, callback, headers=[], method="GET", content=null) {
  49. var request = new XMLHttpRequest();
  50. request.addEventListener("load", callback);
  51. request.open(method, url, true);
  52.  
  53. if (window.__EDPUZZLE_DATA__ && window.__EDPUZZLE_DATA__.token) {
  54. headers.push(["authorization", window.__EDPUZZLE_DATA__.token]);
  55. }
  56. for (const header of headers) {
  57. request.setRequestHeader(header[0], header[1]);
  58. }
  59.  
  60. request.send(content);
  61. }
  62. function createActivationButton() {
  63. // Create a button element
  64. var activationButton = document.createElement("button");
  65.  
  66. // Set the button text
  67. activationButton.textContent = "Activate Edpuzzle Script";
  68.  
  69. // Add a click event listener to the button
  70. activationButton.addEventListener("click", function () {
  71. // Call the init function to activate the Edpuzzle script
  72. init();
  73. });
  74.  
  75. // Create a style for the button
  76. var buttonStyle = `
  77. background-color: #00ADEF;
  78. color: #FFFFFF;
  79. padding: 10px 20px;
  80. font-size: 16px;
  81. border: none;
  82. cursor: pointer;
  83. transition: opacity 0.3s ease-in-out; /* Add a smooth transition effect */
  84.  
  85. `;
  86.  
  87. // Apply the style to the button
  88. activationButton.style.cssText = buttonStyle;
  89.  
  90. // Add a hover effect
  91. activationButton.addEventListener("mouseover", function () {
  92. activationButton.style.opacity = 0.8; // Adjust the opacity as needed
  93. });
  94.  
  95. // Restore the original opacity on mouseout
  96. activationButton.addEventListener("mouseout", function () {
  97. activationButton.style.opacity = 1;
  98. });
  99.  
  100. // Append the button to the body of the document
  101. document.body.appendChild(activationButton);
  102. }
  103.  
  104.  
  105. // Call the function to create the activation button
  106. createActivationButton();
  107.  
  108.  
  109. function init() {
  110. if (window.location.hostname == "edpuzzle.hs.vc") {
  111. alert("To use this, drag this button into your bookmarks bar. Then, run it when you're on an Edpuzzle assignment.");
  112. }
  113. else if ((/https{0,1}:\/\/edpuzzle.com\/assignments\/[a-f0-9]{1,30}\/watch/).test(window.location.href)) {
  114. getAssignment();
  115. }
  116. else if (window.canvasReadyState) {
  117. handleCanvasURL();
  118. }
  119. else if (window.schoologyMoreLess) {
  120. handleSchoologyURL();
  121. }
  122. else {
  123. alert("Please run this script on an Edpuzzle assignment. For reference, the URL should look like this:\nhttps://edpuzzle.com/assignments/{ASSIGNMENT_ID}/watch");
  124. }
  125. }
  126.  
  127. function handleCanvasURL() {
  128. let location_split = window.location.href.split("/");
  129. let url = `/api/v1/courses/${location_split[4]}/assignments/${location_split[6]}`;
  130. http_get(url, function(){
  131. let data = JSON.parse(this.responseText);
  132. let url2 = data.url;
  133.  
  134. http_get(url2, function() {
  135. let data = JSON.parse(this.responseText);
  136. let url3 = data.url;
  137.  
  138. alert(`Please re-run this script in the newly opened tab. If nothing happens, then allow popups on Canvas and try again.`);
  139. open(url3);
  140. });
  141. });
  142. }
  143.  
  144. function handleSchoologyURL() {
  145. let assignment_id = window.location.href.split("/")[4];
  146. let url = `/external_tool/${assignment_id}/launch/iframe`;
  147. http_get(url, function() {
  148. alert(`Please re-run this script in the newly opened tab. If nothing happens, then allow popups on Schoology and try again.`);
  149.  
  150. //strip js tags from response and add to dom
  151. let html = this.responseText.replace(/<script[\s\S]+?<\/script>/, "");
  152. let div = document.createElement("div");
  153. div.innerHTML = html;
  154. let form = div.querySelector("form");
  155.  
  156. let input = document.createElement("input")
  157. input.setAttribute("type", "hidden");
  158. input.setAttribute("name", "ext_submit");
  159. input.setAttribute("value", "Submit");
  160. form.append(input);
  161. document.body.append(div);
  162.  
  163. //submit form in new tab
  164. form.setAttribute("target", "_blank");
  165. form.submit();
  166. div.remove();
  167. });
  168. }
  169.  
  170. function getAssignment(callback) {
  171. var assignment_id = window.location.href.split("/")[4];
  172. if (typeof assignment_id == "undefined") {
  173. alert("Error: Could not infer the assignment ID. Are you on the correct URL?");
  174. return;
  175. }
  176. var url1 = "https://edpuzzle.com/api/v3/assignments/"+assignment_id;
  177.  
  178. http_get(url1, function(){
  179. var assignment = JSON.parse(this.responseText);
  180. if ((""+this.status)[0] == "2") {
  181. openPopup(assignment);
  182. }
  183. else {
  184. alert(`Error: Status code ${this.status} recieved when attempting to fetch the assignment data.`)
  185. }
  186. });
  187. }
  188.  
  189. function openPopup(assignment) {
  190. var media = assignment.medias[0];
  191. var teacher_assignment = assignment.teacherAssignments[0];
  192. var assigned_date = new Date(teacher_assignment.preferences.startDate);
  193. var date = new Date(media.createdAt);
  194. thumbnail = media.thumbnailURL;
  195. if (thumbnail.startsWith("/")) {
  196. thumbnail = "https://"+window.location.hostname+thumbnail;
  197. }
  198.  
  199. var deadline_text;
  200. if (teacher_assignment.preferences.dueDate == "") {
  201. deadline_text = "no due date"
  202. }
  203. else {
  204. deadline_text = "due on "+(new Date(teacher_assignment.preferences.dueDate)).toDateString();
  205. }
  206.  
  207. var base_html = `
  208. <!DOCTYPE html>
  209. <head>
  210. <style>
  211. * {font-family: Arial}
  212. </style>
  213. <script>
  214. var base_url = "${base_url}";
  215. function http_get(url, callback) {
  216. var request = new XMLHttpRequest();
  217. request.addEventListener("load", callback);
  218. request.open("GET", url, true);
  219. request.send();
  220. }
  221. function get_tag(tag, url) {
  222. console.log("Loading "+url);
  223. http_get(url, function(){
  224. if ((""+this.status)[0] == "2") {
  225. var element = document.createElement(tag);
  226. element.innerHTML = this.responseText;
  227. document.getElementsByTagName("head")[0].appendChild(element);
  228. }
  229. else {
  230. console.error("Could not fetch "+url);
  231. }
  232. });
  233. }
  234. get_tag("style", base_url+"/app/popup.css");
  235. get_tag("script", base_url+"/app/popup.js");
  236. get_tag("script", base_url+"/app/videooptions.js");
  237. get_tag("script", base_url+"/app/videospeed.js");
  238. </script>
  239. <title>Answers for: ${media.title}</title>
  240. </head>
  241. <div id="header_div">
  242. <div>
  243. <img src="${thumbnail}" height="108px">
  244. </div>
  245. <div id="title_div">
  246. <p style="font-size: 16px"><b>${media.title}</b></h2>
  247. <p style="font-size: 12px">Uploaded by ${media.user.name} on ${date.toDateString()}</p>
  248. <p style="font-size: 12px">Assigned on ${assigned_date.toDateString()}, ${deadline_text}</p>
  249. <p style="font-size: 12px">Correct choices are <u>underlined</u>.</p>
  250. <input id="skipper" type="button" value="Skip Video" onclick="skip_video();" disabled/>
  251. <input id="answers_button" type="button" value="Answer Questions" onclick="answer_questions();" disabled/>
  252. <div id="speed_container" hidden>
  253. <label style="font-size: 12px" for="speed_dropdown">Video speed:</label>
  254. <select name="speed_dropdown" id="speed_dropdown" onchange="video_speed()">
  255. <option value="0.25">0.25</option>
  256. <option value="0.5">0.5</option>
  257. <option value="0.75">0.75</option>
  258. <option value="1" selected>Normal</option>
  259. <option value="1.25">1.25</option>
  260. <option value="1.5">1.5</option>
  261. <option value="1.75">1.75</option>
  262. <option value="2">2</option>
  263. <option value="-1">Custom</option>
  264. </select>
  265. <label id="custom_speed_label" style="font-size: 12px" for="custom_speed"></label>
  266. <input type="range" id="custom_speed" name="custom_speed" value="1" min="0.1" max="16" step="0.1" oninput="video_speed()" hidden>
  267. </div>
  268. <div id="options_container">
  269. <label for="pause_on_focus" style="font-size: 12px">Don't pause on unfocus: </label>
  270. <input type="checkbox" id="pause_on_focus" name="pause_on_focus" onchange="toggle_unfocus();">
  271. </div>
  272. </div>
  273. </div>
  274. <hr>
  275. <div id="content">
  276. <p style="font-size: 12px" id="loading_text"></p>
  277. </div>
  278. <hr>
  279. `;
  280. popup = window.open("about:blank", "", "width=600, height=400");
  281. popup.document.write(base_html);
  282.  
  283. popup.document.assignment = assignment;
  284. popup.document.dev_env = document.dev_env;
  285. popup.document.edpuzzle_data = window.__EDPUZZLE_DATA__;
  286.  
  287. getMedia(assignment);
  288. }
  289.  
  290. function getMedia(assignment) {
  291. var text = popup.document.getElementById("loading_text");
  292. text.innerHTML = `Fetching assignments...`;
  293.  
  294. var media_id = assignment.teacherAssignments[0].contentId;
  295. var url2 = `https://edpuzzle.com/api/v3/media/${media_id}`;
  296.  
  297. fetch(url2, {credentials: "omit"})
  298. .then(response => {
  299. if (!response.ok) {
  300. var text = popup.document.getElementById("loading_text");
  301. var content = popup.document.getElementById("content");
  302. popup.document.questions = questions;
  303. text.remove();
  304. content.innerHTML += `Error: Status code ${response.status} received when attempting to fetch the answers.`;
  305. }
  306. else return response.json();
  307. })
  308. .then(media => {
  309. parseQuestions(media.questions);
  310. })
  311. }
  312.  
  313. function parseQuestions(questions) {
  314. var text = popup.document.getElementById("loading_text");
  315. var content = popup.document.getElementById("content");
  316. popup.document.questions = questions;
  317. text.remove();
  318.  
  319. if (questions == null) {
  320. content.innerHTML += `<p style="font-size: 12px">Error: Could not get the media for this assignment. </p>`;
  321. return;
  322. }
  323.  
  324. var question;
  325. var counter = 0;
  326. var counter2 = 0;
  327. for (let i=0; i<questions.length; i++) {
  328. for (let j=0; j<questions.length-i-1; j++) {
  329. if (questions[j].time > questions[j+1].time){
  330. let question_old = questions[j];
  331. questions[j] = questions[j + 1];
  332. questions[j+1] = question_old;
  333. }
  334. }
  335. }
  336.  
  337. for (let i=0; i<questions.length; i++) {
  338. question = questions[i];
  339. let choices_lines = [];
  340.  
  341. if (typeof question.choices != "undefined") {
  342. let min = Math.floor(question.time/60).toString();
  343. let secs = Math.floor(question.time%60).toString();
  344. if (secs.length == 1) {
  345. secs = "0"+secs;
  346. }
  347. let timestamp = min+":"+secs;
  348. let question_content;
  349. if (question.body[0].text != "") {
  350. question_content = `<p>${question.body[0].text}</p>`;
  351. }
  352. else {
  353. question_content = question.body[0].html;
  354. }
  355.  
  356. let answer_exists = false;
  357. for (let j=0; j<question.choices.length; j++) {
  358. let choice = question.choices[j];
  359. if (typeof choice.body != "undefined") {
  360. counter++;
  361. let item_html;
  362. if (choice.body[0].text != "") {
  363. item_html = `<p>${choice.body[0].text}</p>`;
  364. }
  365. else {
  366. item_html = `${choice.body[0].html}`;
  367. }
  368. if (choice.isCorrect == true) {
  369. choices_lines.push(`<li class="choice choice-correct">${item_html}</li>`);
  370. answer_exists = true;
  371. }
  372. else {
  373. choices_lines.push(`<li class="choice">${item_html}</li>`);
  374. }
  375. }
  376. }
  377. if (!answer_exists) continue;
  378.  
  379. let choices_html = choices_lines.join("\n");
  380. let table = ``
  381. if (counter2 != 0) {
  382. table += `<hr>`;
  383. }
  384. table += `
  385. <table>
  386. <tr class="header no_vertical_margin">
  387. <td class="timestamp_div no_vertical_margin">
  388. <p>[${timestamp}]</p>
  389. </td>
  390. <td class="question">
  391. ${question_content}
  392. </td>
  393. </tr>
  394. <tr>
  395. <td></td>
  396. <td>
  397. <ul style="margin-top: 6px; margin-bottom: 0px; padding-left: 18px;">
  398. ${choices_html}
  399. </ul>
  400. </td>
  401. </tr>
  402. </table>
  403. `;
  404.  
  405. content.innerHTML += table;
  406. counter2++;
  407. }
  408. }
  409. popup.document.getElementById("skipper").disabled = false;
  410. if (counter == 0 || counter2 == 0) {
  411. content.innerHTML += `<p style="font-size: 12px">No valid multiple choice questions were found.</p>`;
  412. }
  413. else {
  414. popup.document.getElementById("answers_button").disabled = false;
  415. }
  416. popup.questions = questions;
  417. }
  418.  

QingJ © 2025

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